1. Send all outgoing mail to a smart host.

    The default configuration contains this router, which sends outgoing mail directly to its destination, using a DNS lookup:

      lookuphost:
        driver = lookuphost
        transport = remote_smtp
    

    If you're an end user, you may want instead to send everything to your ISP's mail server, to let it handle the routing and queueing. It's easy. Suppose your ISP's mail server is called mail.myisp.com. All you have to do is replace the default router with this one:

      smarthost:
        driver = domainlist
        transport = remote_smtp 
        route_list = *  mail.myisp.com  byname
    

    In that route_list setting, the asterisk means "for all remote domains", and the byname means "look up the server's IP address using the system's name-resolution function (gethostbyname)". (If you want to avoid any kind of lookup, you can put the smart host's IP address instead of the name, but you still need the byname.)

    If your ISP has several mail servers, you can list them all:

        route_list = * \
                     "mail1.myisp.com : \
                     mail2.myisp.com" \
                     byname
    

    We are using backslashes here to continue the configuration lines, and we have to put the colon-separated list of hosts in quotes, because it's got spaces in it. One final trick in this case is to add

        hosts_randomize
    

    to the router. This tells Exim to sort the host list into a random order each time it is used.

  2. Save outgoing mail until a dial-up connection is made.

    If you are running a dial-up host, you need to configure Exim so that it does not try to deliver outgoing mail until you have connected to the Net. (You probably also want to arrange for such mail to be delivered to a smart host, as described above.) To stop Exim trying to do immediate deliveries for remote domains, you must add the line

      queue_remote_domains = *
    

    to the first section of the configuration file. (In this context, * means "all".) When Exim receives a message addressed to a remote domain, it now just leaves it on the queue, and does not attempt to deliver it. The other thing you must do is to stop Exim starting queue runner processes automatically because you want to process the queue only when dialed up. Somewhere in the boot scripts for your host there will be a line of the form /usr/sbin/sendmail -bd -q30m, which starts up the MTA daemon when you reboot. (The path name is historic; on an Exim system, it will normally be a symbolic link to the Exim binary. On some systems /usr/lib/sendmail is used.) The -q30m option specifies that queue runners are started every 30 minutes; you should remove this option from the command. Unless you are going to reboot, you must now kill any existing Exim daemon and restart it. This normally can be done by obeying (as root):

      kill `cat /var/spool/exim/exim-daemon.pid`
    

    Restart the daemon without the -q30m option:

      /usr/sbin/sendmail -bd
    

    So far, so good. You now have a system where all outgoing mail just sits on the queue until some explicit action is taken. How do you get it delivered when you dial up? The command that must be run is exim -qqff.

    This is usually placed in a script that is automatically obeyed once the connection has been made, but you can run it by hand if you want to. However, you must either do this as root, or you must add your login to the exim group in order to give yourself Exim administration privileges.

  3. Route all mail for a domain to a single mailbox.

    How you do this depends on whether the single mailbox is remote or local, and whether it is itself in the domain or not. Let's first take the simple case where the host is handling only one local domain, and you want all mail to be delivered into the mailbox /var/mail/incoming. All you need to do is configure a single director like this:

      all:
        driver = smartuser
        transport = incoming_delivery 
    

    Then create a corresponding transport:

      incoming_delivery:
        driver = appendfile
        file = /var/mail/incoming
        user = mail
    

    You must supply a user setting to tell Exim which uid to run as when writing to the file. Note that the two configuration snippets above must be placed in the correct sections of the configuration file: the first in the "directors" section, and the second in the "transports" section.

    If you are handling several domains, and the relevant mailbox is not itself in the special domain, it is more straightforward to handle this as a kind of aliasing operation. Suppose the domain is onebox.domain.com and its mail must be sent to postmaster@other.domain.com. Firstly, you need to ensure that onebox.domain.com is a treated as a local domain, so you must set (in the first section of the configuration)

      local_domains = onebox.domain.com : ...
    

    where the ... includes all your other local domains. The following director than handles the requirement:

      onebox_director:
        driver = smartuser
        domains = onebox.domain.com
        new_address = postmaster@other.domain.com
    

    This simply forwards everything for the domain to the given address. What if the address is itself in the special domain? You can handle that by listing an exception:

      onebox_director:
        driver = smartuser
        domains = onebox.domain.com
        local_parts = !postmaster 
        new_address = postmaster@onebox.domain.com
    

    That setting of local_parts means "not postmaster". The address postmaster@onebox.domain.com bypasses this director. Of course, you must then have a subsequent director that deals with it.

  4. Give each virtual domain its own alias file.

    The term "virtual domain" usually means a domain in which every valid address is just an alias for some other address, which may be local or remote to the host that handles the virtual domain. A virtual domain is essentially just a redirection service. A common way of managing a number of virtual domains is to give each one its own alias file; this means you can allow each file to be maintained by its own manager. Suppose you are handling the virtual domains a.virt.com, b.virt.com, c.virt.com, etc. You have to make them local domains

      local_domains = *.virt.com : ...
    

    (where the ... indicates your other local domains). Then you can handle them all with a single director:

      virtual:
        driver = aliasfile
        domains = *.virt.com
        file = /etc/aliases.$domain
        search_type = lsearch
        no_more
    

    You would probably put this director first, before all the others. It runs only for addresses whose domains match *.virt.com, and after it has run, no more directors are run because of the no_more setting. When it runs, it expands the file name by inserting the domain, so, for example, if the incoming address is user@a.virt.com, the file it inspects is /etc/aliases.a.virt.com. This file could contain lines like this:

      user1:  some.one@some.domain
      user2:  some.one.else@other.domain
    

    If the local part is not found in the file, delivery fails because no further directors are run (but see the next item).

  5. Add defaults to virtual domains.

    You can add a default address to a virtual domain alias file very easily. First, you must tell Exim to look for a default if it cannot match the local part it is looking for. You do this by specifying

        search_type = lsearch*
    

    instead of just specifying lsearch (as in the previous item), so the complete director might be:

      virtual:
        driver = aliasfile
        domains = *.virt.com
        file = /etc/aliases.$domain
        search_type = lsearch*
        no_more
    

    The added asterisk tells Exim to look for an alias for the string * when its initial search fails, so if you have an alias file like this:

      *:      postmaster@whatever.domain
      user1:  some.one@some.domain
      user2:  some.one.else@other.domain
    

    any local part other than user1 and user2 gets forwarded to postmaster@whatever.domain. Note that the order of the items in the file does not matter. Exim first searches for the local part of the incoming address, and only if that is not found does it search for *.

  6. Convert sendmail alias files.

    If you are converting from sendmail to Exim, you will have to make some changes to alias files if you are using any kind of domain default. There are four different kinds of line that appear in sendmail alias files, of the following forms:

      user1:          newuser1@new.domain1
      user2@domain2:  newuser2@new.domain2
      @domain3:       newuser3@new.domain3
      %1@domain4:     @new.domain4
    

    The first line is an example of an alias for a local part in a local domain. Exim handles these by default. You can put such a line straight into /etc/aliases, and use Exim's default configuration. It matches the given local part in any local domain.

    The second line specifies a complete email address instead of just a local part. Exim normally looks up just the local part when expanding aliases; if you want it to use the full address, you must set the include_domain option, but then it always uses the full address. How can we cope with a mixture of lines of the first two types? The answer is to define two directors in the configuration, like this:

      full_aliases:
        driver = aliasfile
        file = /etc/aliases
        search_type = lsearch
        include_domain
        
      short_aliases:
        driver = aliasfile
        file = /etc/aliases
        search_type = lsearch     
    

    With this configuration, Exim first searches /etc/aliases using the full address. If it doesn't find anything, it searches again, but this time using only the local part.

    The third sendmail alias line is specifying a default for a specific domain. You can do this in Exim in several ways. After the aliasing directors, you could put this director:

      domain_default:
        driver = smartuser
        domains = domain3
        new_address = newuser3@new.domain3
    

    However, you can also handle defaults as part of aliasing. The alias line must be changed to:

      *@domain3:    newuser3@new.domain3
    

    and the full_aliases director changed to:

      full_aliases:
        driver = aliasfile
        file = /etc/aliases
        search_type = lsearch*@
        include_domain
    

    The *@ in the search type tells Exim to look for *@domain if it fails to find the original address in the file. (If it can't find *@domain, it will then go on to look for *.)

    The fourth sendmail alias line routes a message to the same local part, but at a different domain. Again, there are several ways to achieve this effect in Exim. We could use a smartuser director:

      rename_domain:
        driver = smartuser
        domains = domain4
        new_address = ${quote:$local_part}@new.domain4  
    

    Note the use of the quote string expansion operator, to ensure that the local part is correctly quoted if necessary. To do this transformation as part of aliasing, we need to use the defaulting facility again, and also to set expand option so that the string that is looked up is passed through the string expansion mechanism, in order to substitute $local_part. In the alias file we'd put:

      *@domain4:  ${quote:$local_part}@new.domain4
    

    and the director to use the file would be:

      full_aliases:
        driver = aliasfile
        file = /etc/aliases
        search_type = lsearch*@
        include_domain
        expand 
    
  7. Handle mailing lists with external software.

    For all but trivial mailing lists, you should normally make use of proper mailing list software such as Majordomo or SmartList. Exim has to do two things:

    1. It has to recognize messages addressed to mailing lists, and send them off to be handled by the mailing list software.

    2. It has to recognize messages coming back from the mailing list software so that the original sender address can be preserved. (Normally, the sender of a message sent by a local process is forced to the login of the user running the process, which in this case is likely to be a user reserved for the mailing list software.)

    Majordomo configurations usually make use of a number of aliases that are kept in a separate file, and other mailing list software can be configured in the same way. It is easy to get Exim to handle this. Just set up an additional director:

      majordomo_aliases:
        driver = aliasfile
        file = /usr/local/majordomo/lists/majordomo.aliases
        search_type = lsearch
        pipe_transport = address_pipe 
        user = majordom
        group = majordom
    

    The lines in the alias file are of the form

      majordomo:  |/usr/mail/majordomo ...
    

    that is, an alias name followed by a command that starts with a vertical bar (pipe symbol). (For actual details of the format of the commands, you'll have to check the documentation for your mailing list software.) The pipe symbol tells Exim that this is an alias for a command; in order to run it, the director needs three additional options:

        pipe_transport = address_pipe
    

    specifies which transport should be run for the pipe delivery. The default configuration contains:

      address_pipe:
        driver = pipe
        return_output
    

    which runs a pipe transport with default configuration, except that any output generated by the pipe is returned to the sender.

        user = majordom
        group = majordom
    

    specify the user and group under which the pipe is to be run. OK, so we've now arranged for incoming messages that are addressed to any of the mailing list aliases to be piped to the mailing list manager software. What about getting back messages for delivery to the subscribers? The mailing list software can just call /usr/lib/sendmail to send messages, in the same way that any running process can, but there is one small twist. The command that is used is normally something like

      /usr/lib/sendmail -f <sender address> <recipients>
    

    where the -f option defines the original sender address. Exim doesn't honor this option unless the sender is trusted. When an ordinary user process sends a message, the sender is always taken from the login ID. To ensure that the correct sender addresses are used, you must set

      trusted_users = majordom
    

    in Exim's configuration (in the first section).

  8. Verify message senders.

    In the main part of Exim's configuration, if you set the option

      sender_verify = true
    

    Exim will attempt to verify the sender addresses of incoming messages from other hosts, before accepting the message. If verification fails, the message is rejected. This can keep out a certain amount of junk mail, though, of course, it imposes a certain overhead.

  9. Verify message recipients.

    By default, Exim accepts messages from other hosts without checking the validity of recipients, although it does check for unwanted relay attempts (see the next two items). If you set, in the main part of Exim's configuration, the option

      receiver_verify = true
    

    Exim will verify each recipient as it is received. Those that fail verification are refused as part of the SMTP dialogue. If all recipients are rejected, the message does not get into your host at all.

  10. Configure incoming relaying.

    Incoming relaying refers to the case where your host is acting as an incoming gateway server for domains that are handled by other hosts inside your organization, or is acting as a secondary backup MX server for any domains. In these cases, there is a given set of domains that you want to be able to relay. Configuring Exim to do this is simple. Just set

      relay_domains = list of domains
    

    For example,

      relay_domains = *.mycompany.plc.uk : \
                      myfriend.org
    

    Once you've done this, your host will accept mail from outside that is addressed to those domains, and will relay it according to your delivery configuration.

  11. Configure outgoing relaying.

    Outgoing relaying refers to the case where your host is acting as an outgoing gateway server for hosts inside your organization. Usually this means hosts on your local LAN. In this case, you want your host to accept any outgoing mail from these hosts, but no others. Again, the configuration is simple. Just set

      host_accept_relay = list of hosts
    

    The most common form this takes is to specify the hosts by a set of IP addresses, for example:

      host_accept_relay = 192.168.4.0/24 
    

    Once you've done this, your host will accept all mail from the given hosts, whatever the domains of recipients.

    Note the difference between relay_domains and host_accept_relay: the former accepts relay mail for given domains, whereas the latter accepts relay mail from the given hosts. Of course, there is no reason why you should not set both options if you want to. (This is common.)


Philip Hazel grew up in South Africa. He has a Ph.D. in applied mathematics, and he has spent the last 30 years writing general-purpose software for the Computing Service at the University of Cambridge in England. Some major projects were text editors and text formatters for use on an IBM mainframe system. Since moving from the mainframe to Unix around 1990, he has become more and more involved with email. This lead to his starting to develop Exim in 1995, and the PCRE regular expression library two years later. Outside interests include classical music (as a choral singer and late convert to viola playing), music typesetting, working backstage in amateur theatre, and finding nice places to go walking, preferably not as flat as Cambridgeshire. Philip is married, and has three grown sons.


O'Reilly & Associates recently released (July 2001) Exim: The Mail Transfer Agent.