Skip to content

MAIL FROM stage

With the MAIL FROM command, the client opens a mail transaction and gives the envelope sender of the message. The envelope sender is the return path: the address to which delivery status notifications about the message are sent. It is the first point at which the server learns who claims to be sending, so this stage is where senders are accepted, refused or rewritten.

The stage is configured on the MtaStageMail singleton (found in the WebUI under Settings › MTA › Session › MAIL FROM Stage). Its expressions can use the MAIL FROM variables, which add sender, sender_domain and authenticated_as to the connection variables.

Several checks that are configured elsewhere also run at MAIL FROM:

The first decision of the stage is whether to accept the sender at all. isSenderAllowed makes it. When the expression returns false, MAIL FROM is refused with 550 5.7.1 Sender address not allowed. The default accepts every authenticated client. It also accepts an unauthenticated sender, unless the sender’s domain is in the spam-block lookup list:

{
"isSenderAllowed": {"else": "!is_empty(authenticated_as) || !key_exists('spam-block', sender_domain)"}
}

With the default expression, blocking a domain therefore requires no change to the setting. The domain is added as a MemoryLookupKey with the namespace spam-block.

The envelope sender can be replaced with another address, for example to remove an internal subdomain. rewrite does this. When the expression returns an address, that address is used as the sender for the rest of the transaction. An empty string makes the sender the null sender <>. Any other result, such as false, leaves the sender unchanged. Each rewrite is logged as smtp.mail-from-rewritten.

The order of the rewrite matters, because it decides which address each check sees. The rewrite is applied after isSenderAllowed, the Sieve script, milters and hooks, so these see the original sender. It is applied before the sender verification of authenticated users, which sees the rewritten one. Address rewriting describes the rewriting functions and capture groups.

The following expression removes the subdomain from the sender’s domain on every listener except the one named smtp, so that [email protected] becomes [email protected]:

{
"rewrite": {
"match": {"0": {"if": "listener != 'smtp' && matches('^([^@]+)@[^.]+\\.(.+\\..+)$', sender)", "then": "$1 + '@' + $2"}},
"else": "false"
}
}

The backslashes in the regular expression are doubled because the expression is written inside a JSON document. In an expression field of the WebUI, each is written once, as \. (escaping backslashes).

The sender can also be inspected by a system Sieve script. script selects the script to run for each MAIL FROM. The script runs after isSenderAllowed and before the rewrite, the sender verification of authenticated users, the throttles and SPF. Like the milters and MTA hooks at this stage, it therefore sees the sender as the client gave it, as envelope.from, and it can refuse it. The following script refuses a sender by its local part:

require ["envelope", "reject"];
if envelope :localpart :is "from" "known_spammer" {
reject "550 5.7.1 Messages from this sender are not accepted.";
}

Try this script in Sievepad

A rejection refuses only the MAIL FROM command, not the session. The client may therefore start a new transaction with another sender.