Mailfromd |
|
General-Purpose Mail Filter |
Sergey Poznyakoff |
Mailfromd Manual (split by node): | ? |
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 |
The function 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’. That is
exactly then when it signals an exception.
Exceptions are identified by exception types, the decimal
numbers associated with them. The header file ‘status.mfh’
defines symbolic exception names for each of these. The
following table summarizes all the exception types implemented by
mailfromd
version 6.0:
e_dbfailure
General database failure. For example, the database cannot be opened. This exception can be signaled by any function that queries any DBM database.
e_divzero
Division by zero.
e_eof
Function reached end of file while reading. See section I/O functions, for a description of functions that can signal this exception.
e_failure
failure
e_failure
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
(hasmx
, poll
, etc.) or operation (mx matches
).
e_invcidr
Invalid CIDR notation. This is signaled by match_cidr
function
when its second argument is not a valid CIDR.
e_invip
Invalid IP address. This is signaled by match_cidr
function
when its first argument is not a valid IP address.
e_invtime
Invalid time interval specification. It is signaled by
interval
function if its argument is not a valid time interval
(see time interval specification).
e_io
An error occurred during the input-output operation. See section I/O functions, for a description of functions that can signal this exception.
e_macroundef
A Sendmail macro is undefined.
e_noresolve
The argument of a DNS-related function cannot be resolved to host
name or IP address. Currently only ismx
(see ismx) raises
this exception.
e_range
The supplied argument is outside the allowed range. This is
signalled, for example, by substring
function (see substring).
e_regcomp
Regular expression cannot be compiled. This can happen when a
regular expression (a right-hand argument of a matches
operator) is built at the runtime and the produced string is an
invalid regex.
e_ston_conv
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 … |
The if
condition will signal ston_conv
, since ‘10a’
cannot be converted to a number.
e_temp_failure
temp_failure
e_temp_failure
A temporary failure has occurred. This can be signaled by DNS-related functions or operations.
e_url
The supplied URL is invalid. See section 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 success
,
meaning successful return from a function, and not_found
,
meaning that the required entity (e.g. domain name or email address)
was not found. See figure-poll-wrapper, for an illustration on
how these can be used. For consistency with other exception codes,
these can be spelled as e_success
and e_not_found
.
Normally when an exception is signalled, the program execution is
terminated and the MTA is returned a tempfail
status. Additional information regarding the exception is then output
to the logging channel (see section Logging and Debugging). However, the
user can intercept any exception by installing his own
exception-handling routines.
An exception-handling routine is introduced by catch
statement, which has the following syntax:
catch exception-list do handler-body done |
where exception-list is the list of exception types, separated
by the word or
. A special form ‘*’, which stands for
all exceptions, is allowed as well. The handler-body is the list of
statements comprising the handler body. For example, the code below
installs a handler for exceptions 2 (‘e_failure’) and 3
(‘e_temp_failure’):
catch 2 or 3 do … done |
However, using numeric exception codes is not convenient and is not in fact recommended. We recommend to use exception symbolic names instead. These are declared in the header file ‘status.mfh’, which should be included in your startup script. Using this approach, the above code snippet will look like:
#include <status.mfh> catch e_failure or e_temp_failure do … done |
The 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 catch
. Each exception redefined by a catch
remains in force until either another catch appears further in the
same function or handler that redefines it or an end of the containing
block is encountered, whichever occurs first. Upon exit from a
function or milter handler, all exceptions are restored to the state
they had when it has been entered.
Upon entry to a catch
code, it receives two positional
arguments, which can be referenced in handler-body as $1
and $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. A
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 catch
defined within a milter handler must end execution with any of the
following actions: accept
, continue
, discard
,
reject
, tempfail
. By default, continue
is
used.
The following example shows an envfrom
handler that catches
eventual exceptions, signaled by primitive_hasmx
. The handler
accepts the message if the sender domain has any ‘MX’ records, rejects
them if it does not, and continues execution if an exception occurs.
In the latter case, a log message is also issued:
#include <status.mfh> prog envfrom do catch e_failure or e_temp_failure do echo "Caught exception $1: $2" continue done if primitive_hasmx(domainpart($f)) accept else reject fi done |
The following example defines the function hasmx
that
returns true if the domain part of its argument has any ‘MX’ records, and
false if it does not or if an exception occurs (21).
func hasmx (s) returns n do catch * do return 0 done return primitive_hasmx(domainpart($1)) done |
All variables remain visible within catch
body, with the
exception of positional arguments of the enclosing function or
handler. So, if you wish to access function arguments from a
catch
, define your function using new style
(see section User-Defined Functions) declaration. To access positional
arguments of a handler, you will have to assign them to local
variables prior to installing a catch, e.g.:
prog header do string hname $1 string hvalue $2 catch * do echo "Exception $1 while processing header %hname: %hvalue" echo $2 tempfail done … |
You can also generate (or raise) exceptions explicitly in the
filter code, using throw
statement:
throw excode descr |
The arguments correspond exactly to the positional arguments 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
nested statements.
Notice several limitations of throw
: first, the excode
argument must be an immediate value, you cannot use an expression in
its place. Secondly, its range is limited to exception codes,
described in status.mfh. In other words, you cannot (yet)
invent your own exception types.
Mailfromd Manual (split by node): | ? |
Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.