Previous   Next   Contents       (Exim 4.30 Specification)

10. Domain, host, address, and local part lists

A number of Exim configuration options contain lists of domains, hosts, email addresses, or local parts. For example, the hold_domains option contains a list of domains whose delivery is currently suspended. These lists are also used as data in ACL statements (see chapter 38).

Each item in one of these lists is a pattern to be matched against a domain, host, email address, or local part, respectively. In the sections below, the different types of pattern for each case are described, but first we cover some general facilities that apply to all four kinds of list.

10.1. Expansion of lists

Each list is expanded as a single string before it is used. If the expansion is forced to fail, Exim behaves as if the item it is testing (domain, host, address, or local part) is not in the list. Other expansion failures cause temporary errors.

If an item in a list is a regular expression, backslashes, dollars and possibly other special characters in the expression must be protected against misinterpretation by the string expander. The easiest way to do this is to use the \N expansion feature to indicate that the contents of the regular expression should not be expanded. For example, in an ACL you might have:

  deny senders = \N^\d{8}\w@.*\.baddomain\.example$\N :
                 ${lookup{$domain}lsearch{/badsenders/bydomain}}

The first item is a regular expression that is protected from expansion by \N, whereas the second uses the expansion to obtain a list of unwanted senders based on the receiving domain.

After expansion, the list is split up into separate items for matching. Normally, colon is used as the separator character, but this can be varied if necessary, as described in section 6.15.

10.2. Negated items in lists

Items in a list may be positive or negative. Negative items are indicated by a leading exclamation mark, which may be followed by optional white space. A list defines a set of items (domains, etc). When Exim processes one of these lists, it is trying to find out whether a domain, host, address, or local part (respectively) is in the set that is defined by the list. It works like this:

The list is scanned from left to right. If a positive item is matched, the subject that is being checked is in the set; if a negative item is matched, the subject is not in the set. If the end of the list is reached without the subject having matched any of the patterns, it is in the set if the last item was a negative one, but not if it was a positive one. For example, the list in

  domainlist relay_domains = !a.b.c : *.b.c

matches any domain ending in .b.c except for a.b.c. Domains that match neither a.b.c nor *.b.c do not match, because the last item in the list is positive. However, if the setting were

  domainlist relay_domains = !a.b.c

then all domains other than a.b.c would match because the last item in the list is negative. In other words, a list that ends with a negative item behaves as if it had an extra item :* on the end.

Another way of thinking about positive and negative items in lists is to read the connector as “or” after a positive item and as “and” after a negative item.

10.3. File names in lists

If an item in a domain, host, address, or local part list is an absolute file name (beginning with a slash character), each line of the file is read and processed as if it were an independent item in the list, except that further file names are not allowed, and no expansion of the data from the file takes place. Empty lines in the file are ignored, and the file may also contain comment lines:

Putting a file name in a list has the same effect as inserting each line of the file as an item in the list (blank lines and comments excepted). However, there is one important difference: the file is read each time the list is processed, so if its contents vary over time, Exim's behaviour changes.

If a file name is preceded by an exclamation mark, the sense of any match within the file is inverted. For example, if

  hold_domains = !/etc/nohold-domains

and the file contains the lines

  !a.b.c
  *.b.c

then a.b.c is in the set of domains defined by hold_domains, whereas any domain matching *.b.c is not.

10.4. An lsearch file is not an out-of-line list

As will be described in the sections that follow, lookups can be used in lists to provide indexed methods of checking list membership. There has been some confusion about the way lsearch lookups work in lists. Because an lsearch file contains plain text and is scanned sequentially, it is sometimes thought that it is allowed to contain wild cards and other kinds of non-constant pattern. This is not the case. The keys in an lsearch file are always fixed strings, just as for any other single-key lookup type.

If you want to use a file to contain wild-card patterns that form part of a list, just give the file name on its own, without a search type, as described in the previous section.

10.5. Named lists

A list of domains, hosts, email addresses, or local parts can be given a name which is then used to refer to the list elsewhere in the configuration. This is particularly convenient if the same list is required in several different places. It also allows lists to be given meaningful names, which can improve the readability of the configuration. For example, it is conventional to define a domain list called local_domains for all the domains that are handled locally on a host, using a configuration line such as

  domainlist local_domains = localhost:my.dom.example

Named lists are referenced by giving their name preceded by a plus sign, so, for example, a router that is intended to handle local domains would be configured with the line

  domains = +local_domains

The first router in a configuration is often one that handles all domains except the local ones, using a configuration with a negated item like this:

  dnslookup:
    driver = dnslookup
    domains = ! +local_domains
    transport = remote_smtp
    no_more

The four kinds of named list are created by configuration lines starting with the words domainlist, hostlist, addresslist, or localpartlist, respectively. Then there follows the name that you are defining, followed by an equals sign and the list itself. For example:

  hostlist    relay_hosts = 192.168.23.0/24 : my.friend.example
  addresslist bad_senders = cdb;/etc/badsenders

A named list may refer to other named lists:

  domainlist  dom1 = first.example : second.example
  domainlist  dom2 = +dom1 : third.example
  domainlist  dom3 = fourth.example : +dom2 : fifth.example

Warning: If the last item in a referenced list is a negative one, the effect may not be what you intended, because the negation does not propagate out to the higher level. For example, consider:

  domainlist  dom1 = !a.b
  domainlist  dom2 = +dom1 : *.b

The second list specifies “either in the dom1 list or *.b”. The first list specifies just “not a.b”, so the domain x.y matches it. That means it matches the second list as well. The effect is not the same as

  domainlist  dom2 = !a.b : *.b

where x.y does not match. It's best to avoid negation altogether in referenced lists if you can.

Named lists may have a performance advantage. When Exim is routing an address or checking an incoming message, it caches the result of tests on named lists. So, if you have a setting such as

  domains = +local_domains

on several of your routers or in several ACL statements, the actual test is done only for the first one. However, the caching works only if there are no expansions within the list itself or any sublists that it references. In other words, caching happens only for lists that are known to be the same each time they are referenced.

By default, there may be up to 16 named lists of each type. This limit can be extended by changing a compile-time variable. The use of domain and host lists is recommended for concepts such as local domains, relay domains, and relay hosts. The default configuration is set up like this.

10.6. Named lists compared with macros

At first sight, named lists might seem to be no different from macros in the configuration file. However, macros are just textual substitutions. If you write

  ALIST = host1 : host2
  auth_advertise_hosts = !ALIST

it probably won't do what you want, because that is exactly the same as

  auth_advertise_hosts = !host1 : host2

Notice that the second host name is not negated. However, if you use a host list, and write

  hostlist alist = host1 : host2
  auth_advertise_hosts = ! +alist

the negation applies to the whole list, and so that is equivalent to

  auth_advertise_hosts = !host1 : !host2

10.7. Domain lists

Domain lists contain patterns that are to be matched against a mail domain. The following types of item may appear in domain lists:

Here is an example that uses several different kinds of pattern:

  domainlist funny_domains = \
    @ : \
    lib.unseen.edu : \
    *.foundation.fict.example : \
    \N^[1-2]\d{3}\.fict\.example$\N : \
    partial-dbm;/opt/data/penguin/book : \
    nis;domains.byname : \
    nisplus;[name=$domain,status=local],domains.org_dir

There are obvious processing trade-offs among the various matching modes. Using an asterisk is faster than a regular expression, and listing a few names explicitly probably is too. The use of a file or database lookup is expensive, but may be the only option if hundreds of names are required. Because the patterns are tested in order, it makes sense to put the most commonly matched patterns earlier.

10.8. Host lists

Host lists are used to control what remote hosts are allowed to do. For example, some hosts may be allowed to use the local host as a relay, and some may be permitted to use the SMTP ETRN command. Hosts can be identified in two different ways, by name or by IP address. In a host list, some types of pattern are matched to a host name, and some are matched to an IP address. You need to be particularly careful with this when single-key lookups are involved, to ensure that the right value is being used as the key.

10.9. Special host list patterns

If a host list item is the empty string, it matches only when no remote host is involved. This is the case when a message is being received from a local process using SMTP on the standard input, that is, when a TCP/IP connection is not used.

The special pattern “*” in a host list matches any host or no host. Neither the IP address nor the name is actually inspected.

10.10. Host list patterns that match by IP address

If an IPv4 host calls an IPv6 host and the call is accepted on an IPv6 socket, the incoming address actually appears in the IPv6 host as “::ffff:<v4address>”. When such an address is tested against a host list, it is converted into a traditional IPv4 address first. (Not all operating systems accept IPv4 calls on IPv6 sockets, as there have been some security concerns.)

The following types of pattern in a host list check the remote host by inspecting its IP address:

10.11. Host list patterns for single-key lookups by host address

When a host is to be identified by a single-key lookup of its complete IP address, the pattern takes this form:

  net-<single-key-search-type>;<search-data>

For example:

  hosts_lookup = net-cdb;/hosts-by-ip.db

The text form of the IP address of the subject host is used as the lookup key. IPv6 addresses are converted to an unabbreviated form, using lower case letters, with dots as separators because colon is the key terminator in lsearch files. [Colons can in fact be used in keys in lsearch files by quoting the keys, but this is a facility that was added later.] The data returned by the lookup is not used.

Single-key lookups can also be performed using masked IP addresses, using patterns of this form:

  net<number>-<single-key-search-type>;<search-data>

For example:

  net24-dbm;/networks.db

The IP address of the subject host is masked using <number> as the mask length. A textual string is constructed from the masked value, followed by the mask, and this is used as the lookup key. For example, if the host's IP address is 192.168.34.6, the key that is looked up for the above example is “192.168.34.0/24”. IPv6 addresses are converted to a text value using lower case letters and dots as separators instead of the more usual colon, because colon is the key terminator in lsearch files. Full, unabbreviated IPv6 addresses are always used.

Warning: Specifing net32- (for an IPv4 address) or net128- (for an IPv6 address) is not the same as specifing just net- without a number. In the former case the key strings include the mask value, whereas in the latter case the IP address is used on its own.

10.12. Host list patterns that match by host name

There are several types of pattern that require Exim to know the name of the remote host. These are either wildcard patterns or lookups by name. (If a complete hostname is given without any wildcarding, it is used to find an IP address to match against, as described in the section 10.10 above.)

If the remote host name is not already known when Exim encounters one of these patterns, it has to be found from the IP address. By default, Exim first does a reverse DNS lookup; if no name is found in the DNS, the system function (gethostbyaddr() or getipnodebyaddr() if available) is tried. The order in which these lookups are done can be changed by setting the host_lookup_order option.

Although many sites on the Internet are conscientious about maintaining reverse DNS data for their hosts, there are also many that do not do this. Consequently, a name cannot always be found, and this may lead to unwanted effects. For this reason, matching against host names is not as common as matching against IP addresses.

If no name can be found for the IP address, Exim behaves as if the host does not match the list. This may not always be what you want to happen. To change Exim's behaviour, the special item “+include_unknown” may appear in the list (at top level – it is not recognized in an indirected file). If any subsequent items require a host name, but no name can be found, Exim behaves as if the host does match the list. For example,

  host_reject_connection = +include_unknown:*.enemy.ex

rejects connections from any host whose name matches *.enemy.ex, and also any hosts whose name it cannot find.

Warning: Take care when configuring host lists with wildcarded name patterns. Consider what will happen if a name cannot be found.

As a result of aliasing, hosts may have more than one name. When processing any of the following types of pattern, all the host's names are checked:

10.13. Host list patterns for single-key lookups by host name

If a pattern is of the form

  <single-key-search-type>;<search-data>

for example

  dbm;/host/accept/list

a single-key lookup is performend, using the host name as its key. If the lookup succeeds, the host matches the item. The actual data that is looked up is not used.

Reminder: With this kind of pattern, you must have host names as keys in the file, not IP addresses. If you want to do lookups based on IP addresses, you must precede the search type with “net-” (see section 10.11). There is, however, no reason why you could not use two items in the same list, one doing an address lookup and one doing a name lookup, both using the same file.

10.14. Host list patterns for query-style lookups

If a pattern is of the form

  <query-style-search-type>;<query>

the query is obeyed, and if it succeeds, the host matches the item. The actual data that is looked up is not used. The variables $sender_host_address and $sender_host_name can be used in the query. For example:

  hosts_lookup = pgsql;\
    select ip from hostlist where ip='$sender_host_address'

The value of $sender_host_address for an IPv6 address uses colon separators. You can use the sg expansion item to change this if you need to. If you want to use masked IP addresses in database queries, you can use the mask expansion operator.

If the query contains a reference to $sender_host_name, Exim automatically looks up the host name if has not already done so. (See section 10.12 for comments on finding host names.)

Historical note: prior to release 4.30, Exim would always attempt to find a host name before running the query, unless the search type was preceded by net-. This is no longer the case. For backwards compatibility, net- is still recognized for query-style lookups, but its presence or absence has no effect. (Of course, for single-key lookups, net- is important.)

10.15. Mixing wildcarded host names and addresses in host lists

If you have name lookups or wildcarded host names and IP addresses in the same host list, you should normally put the IP addresses first. For example, in an ACL you could have:

  accept hosts = 10.9.8.7 : *.friend.example

The reason for this lies in the left-to-right way that Exim processes lists. It can test IP addresses without doing any DNS lookups, but when it reaches an item that requires a host name, it fails if it cannot find a host name to compare with the pattern. If the above list is given in the opposite order, the accept statement fails for a host whose name cannot be found, even if its IP address is 10.9.8.7.

If you really do want to do the name check first, and still recognize the IP address, you can rewrite the ACL like this:

  accept hosts = *.friend.example
  accept hosts = 10.9.8.7

If the first accept fails, Exim goes on to try the second one. See chapter 38 for details of ACLs.

10.16. Address lists

Address lists contain patterns that are matched against mail addresses. There is one special case to be considered: the sender address of a bounce message is always empty. You can test for this by providing an empty item in an address list. For example, you can set up a router to process bounce messages by using this option setting:

  senders = :

The presence of the colon creates an empty item. If you do not provide any data, the list is empty and matches nothing. The empty sender can also be detected by a regular expression that matches an empty string.

The following kinds of pattern are supported in address lists:

Warning: there is an important difference between the address list items in these two examples:

  senders = +my_list
  senders = *@+my_list

In the first one, my_list is a named address list, whereas in the second example it is a named domain list.

10.17. Case of letters in address lists

Domains in email addresses are always handled caselessly, but for local parts case may be significant on some systems (see caseful_local_part for how Exim deals with this when routing addresses). However, RFC 2505 (Anti-Spam Recommendations for SMTP MTAs) suggests that matching of addresses to blocking lists should be done in a case-independent manner. Since most address lists in Exim are used for this kind of control, Exim attempts to do this by default.

The domain portion of an address is always lowercased before matching it to an address list. The local part is lowercased by default, and any string comparisons that take place are done caselessly. This means that the data in the address list itself, in files included as plain file names, and in any file that is looked up using the “@@” mechanism, can be in any case. However, the keys in files that are looked up by a search type other than lsearch (which works caselessly) must be in lower case, because these lookups are not case-independent.

To allow for the possibility of caseful address list matching, if an item in an address list is the string “+caseful”, the original case of the local part is restored for any comparisons that follow, and string comparisons are no longer case-independent. This does not affect the domain, which remains in lower case. However, although independent matches on the domain alone are still performed caselessly, regular expressions that match against an entire address become case-sensitive after “+caseful” has been seen.

10.18. Local part lists

Case-sensitivity in local part lists is handled in the same way as for address lists, as just described. The “+caseful” item can be used if required. In a setting of the local_parts option in a router with caseful_local_part set false, the subject is lowercased and the matching is initially case-insensitive. In this case, “+caseful” will restore case-sensitive matching in the local part list, but not elsewhere in the router. If caseful_local_part is set true in a router, matching in the local_parts option is case-sensitive from the start.

If a local part list is indirected to a file (see section 10.3), comments are handled in the same way as address lists – they are recognized only if the # is preceded by white space or the start of the line. Otherwise, local part lists are matched in the same way as domain lists, except that the special items that refer to the local host (@, @[], @mx_any, @mx_primary, and @mx_secondary) are not recognized. Refer to section 10.7 for details of the other available item types.


Previous  Next  Contents       (Exim 4.30 Specification)