FEATURE(domainmap) Macro for sendmail


Introduction

The existing virtusertable feature distributed with sendmail is a good basic approach to virtual hosting, but it is missing a few key features:
  1. Ability to have a different map for each domain.
  2. Ability to perform virtual hosting for domains which are not in $=w.
  3. Ability to use a centralized network-accessible database (such as PH) which is keyed on username alone (as opposed to the fully-qualified email address).
The domainmap feature neatly solves these problems.


Downloading

The FEATURE(domainmap) macro is included with sendmail-8.10 or newer in the contrib directory. To use it, simply move the contrib/domainmap.m4 file to the cf/feature directory.

Starting with sendmail-8.11.2, the macro uses a regex map to find the map for a given domain instead of the dequote map. This is needed if your domain has a "-" (dash) character in it. However, if your domain does not have any dashes and you do not wish to use the regex map, you can define the m4 variable DOMAINMAP_NO_REGEX before calling FEATURE(domainmap).

(If you have compiled sendmail-8.11.2 with -D_FFR_ADDR_TYPE, you may wish to use this alternate version. If you define the m4 variable DOMAINMAP_ADDRTYPE before calling FEATURE(domainmap), the macro will use the $&{addr_type} variable to avoid a map lookup on the sender address.)

If you haven't yet upgraded to sendmail-8.10, here's a patch to add an older version of the FEATURE(domainmap) macro to sendmail-8.9.3:

	sendmail-8.9.3-FEATURE-domainmap-2.diff
To install the patch, you will need to download the sendmail-8.9.3 source code. Then, apply the patch like this:
	gzip -dc sendmail.8.9.3.tar.gz | tar xpf -
	patch -p0 < sendmail-8.9.3-FEATURE-domainmap-2.diff


Using the Macro

The basic syntax of the macro is:
	FEATURE(domainmap, `domain.com', `map definition ...')dnl
To illustrate how it works, here is an example:
	FEATURE(domainmap, `foo.com', `dbm -o /etc/mail/foo-users')dnl
In this example, mail sent to user@foo.com will be rewritten by the domainmap. The username will be looked up in the DBM map /etc/mail/foo-users, which looks like this:
	jsmith	johnsmith@mailbox.foo.com
	jdoe	janedoe@sandbox.bar.com
So mail sent to jsmith@foo.com will be relayed to johnsmith@mailbox.foo.com, and mail sent to jdoe@foo.com will be relayed to janedoe@sandbox.bar.com.

The FEATURE(domainmap) Macro supports the user+detail syntax by stripping off the +detail portion before the domainmap lookup and tacking it back on to the result. Using the example above, mail sent to jsmith+sometext@foo.com will be rewritten as johnsmith+sometext@mailbox.foo.com.

If one of the elements in the $=w class (i.e., "local" delivery hosts) is a domain specified in a FEATURE(domainmap) entry, you need to use the LOCAL_USER(username) macro to specify the list of users for whom domainmap lookups should not be done.


How It Works

The FEATURE(domainmap) Macro uses the LOCAL_RULESETS directive to add a ruleset called DomainMapLookup which performs domainmap lookups. It also adds hooks to call DomainMapLookup in ruleset 98 using the LOCAL_RULE_0 directive. These rulesets are only added once, no matter how many times the macro is called.

Each time the macro is called, it adds the domain name given in its first argument to $=D. It also creates a new map of the type given in its second argument, with -T<TEMP> appended. The name of the map for a given domain is determined by changing all dots to underscores, and prepending the string domain_. For example, the map for uiuc.edu is called domain_uiuc_edu.

When sendmail is parsing an address, the rules in ruleset 98 call DomainMapLookup for any address whose host portion is in $=D. The DomainMapLookup ruleset then performs the map lookup and uses ruleset 97 to recursively canonify and parse the result. Because of this recursion, the result of one domainmap lookup can cause another domainmap lookup, so you have to be careful to avoid infinite loops.


Mark D. Roth <roth@feep.net>