(Ab)using Exim routers for their full power

November 13, 2009

Officially, as reflected in the documentation, Exim routers are expected to take more or less disjoint sets of addresses; for example, you have one router to do DNS lookups and SMTP for external addresses, one router to handle aliases, one router to expand the .forwards of people with them, and one router to deliver to people's mailboxes for people without .forwards. This makes the ordering of the routers relatively unimportant; approached this way, it is used mostly to make writing routers more convenient by having to be less neurotically careful about what addresses a router applies to.

(There is one exception; traditional .forward handling absolutely requires ordering and cannot be done with router conditions.)

If you want to really do powerful things with Exim routers, you need to go beyond this view. Instead, you should think of routers as (conditional) steps, or decision points, in a peculiar programming language. Not all decision points apply (or potentially apply) to all addresses, but it is entirely natural that multiple routers potentially apply (depending on circumstances) to the same set of addresses; each such router is a step on the conditional handling logic for these addresses.

(This mindset sounds simple when I explain it, but I don't think that it's obvious from the current Exim documentation. I've certainly seen a fair number of 'how to do X' questions asked on the Exim mailing list by people who clearly hadn't made this conceptual leap.)

Once you think of routers this way, ordering becomes important; for routers that handle the same set of addresses, the relative ordering of the routers is the ordering of decision steps about those addresses. Often you have something close to a total order of routers because you will want to do some common things with all addresses.

To make all of this less abstract, here is the list of decisions that our central mail system makes about external addresses, each implemented with a separate router:

  1. is this a locally generated bounce of a spam message? discard if so
  2. is this a looping bounce message? discard if so
  3. is all further handling of this address being manually deferred?
  4. if this is a spam message, has it exceeded the timeout interval for this address's domain? bounce if so
  5. route the address with DNS lookups and deliver the message via SMTP

(Some but not all of these also apply to internal addresses too.)

Sidebar: why .forward handling requires ordering

I cheated in the my example description of Exim routers. Traditional .forward semantics allow you to put your own email address in your .forward again; this means 'deliver to me, bypassing my .forward', which usually winds up putting a copy of the message in /var/mail. If you want to support these semantics under Exim, the router that delivers messages to /var/mail cannot apply only to people who do not have .forwards, and thus has to be ordered after the router that handles .forwards.

(How Exim makes these semantics work is a little bit complicated.)

Written on 13 November 2009.
« What makes Exim work as a mailer construction kit
How to defer things in Exim »

Page tools: View Source, Add Comment.
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Fri Nov 13 00:09:56 2009
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.