Chapter 3. Getting Email Out of the Queue

This chapter covers the output end of the qmail mail system, or how email is delivered by qmail and how it leaves the message queue. The following figure describes this part of the overall structure of qmail described in Chapter 1.

Getting Email Out of the Queue

qmail-send and the Qmail Queue

The primary function of the on-disk queue is to serve as a reliable storage and signaling mechanism for the qmail-send program, which is the heart of the qmail queuing system. The qmail-send program's most fundamental task is to make the primary routing decision: whether a given email should be delivered locally or remotely. This decision is made exactly once per recipient, and is stored in the queue with the email.

The qmail-send program can be thought of as a military general, commanding the qmail delivery army. As a general, it has two sergeants: qmail-lspawn and qmail-rspawn. Depending on whether a given email should be delivered locally or remotely, delivery commands for that email are given to either qmail-lspawn (for local deliveries) or qmail-rspawn (for remote deliveries). Like qmail-send, these two programs make key decisions and then delegate responsibility to the foot soldiers of the qmail delivery army: qmail-local and qmail-remote. The qmail-send program limits the concurrency of deliveries performed by qmail-lspawn and qmail-rspawn, according to the content of the control files control/concurrencyremote (defaults to 20 concurrent deliveries) and control/concurrencylocal (defaults to 10 concurrent deliveries).

Determining whether an address is local or remote is based on two control files: control/locals and control/virtualdomains. The control/locals file is a list of local domains, one per line. Any address whose host part (after the @ symbol) is listed in control/locals is considered local. The control/virtualdomains file specifies patterns that will match against domains, domain-suffixes, and users at specific domains. Any address matching a pattern in the control/virtualdomains file is considered to be a virtual address. Virtual addresses are handled in a manner similar to local addresses—they are not relayed to another machine and are delivered locally.

Once an email is delivered, either remotely or locally, it is removed from the on-disk queue by qmail-clean at the behest of qmail-send. If any messages fail to be delivered (in a temporary way, for example if the destination server could not be contacted), the qmail-send program retries sending them according to an exponential back-off schedule. If a message remains in the queue for too long, or was permanently rejected during delivery, qmail-send creates a bounce message to be sent to the original message's sender.

Delivering Email Locally

The qmail-lspawn program is given delivery commands for local messages. Each delivery command consists of a message number, a sender, and a recipient. It uses first the qmail-users mechanism and, if necessary, the qmail-getpw program (the qmail interface to UNIX system users) to locate the home directory and other relevant details about each recipient. Then qmail-lspawn spawns a qmail-local instance to deliver to that user. The qmail-local instance is spawned asynchronously, so the actual deliveries can occur in any order. In order to execute deliveries safely and securely, the qmail-local program must run with the UNIX User Identification Number (UID) and UNIX Group Identification Number (GID) of the recipient. Thus, qmail-lspawn must run as the root user, so that when spawning the qmail-local program to do the delivery, qmail-lspawn can change to the necessary UID.

One thing to note is that because qmail-lspawn runs as root and qmail-local runs as the user to whom the mail is being delivered, qmail-local cannot read messages out of the on-disk queue but qmail-lspawn can. Thus, part of qmail-lspawn's job is to open the message in the queue and hand it to qmail-local.

The Default

When qmail-lspawn starts via qmail-start, it receives a single argument—a default delivery instruction. This instruction is passed on to each qmail-local instance as they are created. In the absence of further delivery instructions, qmail-local uses this method to deliver the email it is given. This default instruction can be as complex as a .qmail file; indeed, the default instruction format is identical to the format of the contents of a .qmail file. In a typical install, this default is specified in the rc file, which is the script that uses qmail-start to set up all of the persistent sending processes (qmail-send, qmail-lspawn, qmail-rspawn, and qmail-clean).

.qmail Files

The qmail-local program's task is to first locate the correct .qmail file for the recipient and then use that file's contents (or, if there isn't one, the default instructions) as a list of instructions for delivering the email.

The syntax of these files is very simple, but allows for some rather complex behavior. Every line of the .qmail file is either a delivery instruction or a comment. Comments are lines that begin with a hash mark (#).

There are three types of delivery instructions that are processed in the order they appear in the file:

  • A forward

  • A file or folder to deliver to

  • A pipe to a program

Forwards

The first type of delivery instruction is the simplest. To tell qmail to deliver the message to a different address, simply create a line containing only an email address to which it must be delivered. In cases where a given address is confusing, putting an ampersand (&) at the beginning of the line makes it explicit. For example:

Extraneous spaces or extra comments cause qmail to complain about malformed delivery instructions. If the new recipient address is a local address, only the account name is necessary.

Maildirs and mboxes

The second type of delivery instruction is also simple, but has a convenient twist. Files and folders can be specified as either full paths or relative paths. A full path begins with a forward slash and a relative path begins with a period. Relative paths are relative from the user's home directory. For example:

# This is a full path
/home/user/mailbox
# This is a relative path
./mailbox

If these instructions are in the user's .qmail file, two copies of the message are delivered to the mailbox file in the user's home directory. Qmail delivers email in one of the following two formats: mbox and Maildir. A full discussion of the comparative merits of these two formats is given in Chapter 4. For now, it is sufficient to say that an mbox is a single file containing multiple email messages. It is specified in a delivery instruction as any file whose name does not end in a slash. A Maildir is a directory-based mail storage format. It is specified in a delivery instruction as any file whose name ends in a slash. For example:

# This is an mbox in the user's home directory
./Mail
# This is a Maildir in the user's home directory
./Mail/

Before messages are delivered to a mailbox (either mbox- or Maildir-formatted), qmail tags them with a Delivered-To header indicating the name of the recipient and a Return-Path header, indicating the original envelope sender. Messages delivered to an mbox also receive the mbox-required "From_" header (which is different than the standard From header).

It is important to note that getting rid of email by specifying delivery to /dev/null is not a good idea. The reason for this is that qmail will treat /dev/null as an mbox that needs to be locked (with flock()) before delivery takes place. Unfortunately, according to the POSIX standard, flock() cannot operate on special files like /dev/null. Even if an operating system allows it, this slows down the process of discarding the message, because multiple messages that need to be discarded simultaneously are discarded one at a time. A better way to tell qmail-local to discard the email is to create a .qmail file containing only comments. Note that an empty .qmail file is treated as if it does not exist (and thus the default delivery instructions are used). However, if the file contains only comments (lines that start with a hash mark), it overrides the default delivery instructions and causes qmail-local to discard the email.

Pipes and Programs

The third type of delivery instruction is deceptively simple in appearance. Any line that begins with a vertical bar, also known as a pipe, indicates that the rest of the line should be interpreted by /bin/sh and the content of the email provided on standard input.

For example, a simple program that only discards the email (this is not the best way to discard email) is something like this:

| cat > /dev/null

A common use for this type of delivery instruction is to use a program to create vacation messages. For example, the following delivery instructions deliver the message to a mailbox and create a vacation message:

# deliver it
./Mail
# send to the "vacation" program (may not work, see below) | vacation

Unlike what happens when messages are saved to an mbox or Maildir, emails sent to programs in this manner are not prepended with the Delivered-To, Return-Path, or From_ headers. If these headers are needed, use the preline program to add these headers and then send the message to the command specified in its arguments. For example, most vacation programs require the use of the preline program to operate correctly:

# generate vacation message
| preline vacation

If a delivery program specified in the .qmail file sends anything to standard output, this output is collected and logged in the qmail-send log file. For example, the following will save message Subject headers in the log:

# log the subject |
grep '^Subject: ' 

Actually, the above instruction will log all lines in the email that start with "Subject:". If, for example, the message contains a forwarded message in its body that also has a Subject header, both will be logged. Another complication is that long subjects are sometimes split into multiple lines, and grep will only report the first line. Instead of grep, a better tool to use for this purpose is the 822field program in the mess822 package (http://cr.yp.to/mess822.html):

# log the subject
| 822field Subject

The program specified by the delivery instruction doesn't have to do anything with the email it is given. For example:

| echo this goes in the log!

Environment variables can be used in these commands. Qmail provides several helpful environment variables. For example, the following logs the sender and recipient of messages (this is just an example; that information is part of the standard log):

# log the sender and recipient
| echo $SENDER sent a message to $RECIPIENT

Qmail provides the following environment variables:

Environment Variable

Description

SENDER

The envelope sender address.

NEWSENDER

The sender used for forwarding messages (i.e. delivery instructions beginning with an ampersand&).

RECIPIENT

The envelope recipient address; local@domain.

LOCAL

The local part of the RECIPIENT address.

USER

The user receiving the message.

HOME

The receiving user's home directory.

HOST

The domain part of the RECIPIENT address.

HOST2

The portion of HOST preceding the last dot. For example, if HOST is server1.example.com, HOST2 will be server1.example.

HOST3

The portion of HOST2 preceding the last dot, if there is one. From the previous example, HOST3 is server1. However, if HOST is something shorter, like example.com, HOST3 is undefined.

HOST4

The portion of HOST3 preceding the last dot, if there is one.

EXT

The address extension. For example, if LOCAL is user-extension1-extension2, then EXT is extension1-extension2.

EXT2

The second part of the address extension, if there is one, otherwise EXT2 is undefined. From the above example, EXT2 is extension2.

EXT3

The third part of the address extension, if there is one.

EXT4

The fourth part of the address extension, if there is one.

DEFAULT

The part of EXT that matched against a default .qmail filename, if there is one.

DTLINE

The Delivered-To header that may be added to the message.

RPLINE

The Return-Path header that may be added to the message.

UFLINE

The UUCP-style From_ header that may be added to the message.

Commands specified after the pipe symbol can use any syntax that /bin/sh understands, including conditionals and pipes. For example:

# sanitize the subject before logging it
| 822field Subject | sed s/unspeakable/____/g

Another good example is a script that replaces the behavior of the preline program:

# generate vacation message
| ( echo "$UFLINE$RPLINE$DTLINE"; cat - ) | vacation

It is important to note that the output of one delivery instruction cannot be used as input to any of the others. Each delivery instruction receives an identical copy of the email. One of the ways to work around this is to use multiple addresses. The first recipient address will receive the message and pipe it to a filter, which will then requeue the message to be delivered to another address, which can do multiple deliveries based on the filtered form of the message. A filter behaving in this manner looks something like the following:

#
filter and forward
# remove all instances of the word "foo"
| sed s/<foo>//g | forward $LOCAL-filtered@$HOST

The final twist that makes .qmail files a viable form of mail filtering is the exit code of the programs run. The exit codes of programs called by qmail-local are used to control progress through the .qmail file. Specifically, an exit code of 0 indicates

a successful delivery, while an exit code of 99 indicates that the mail was delivered successfully but that none of the subsequent delivery instructions should be followed. An exit code of 100 indicates that the delivery failed permanently (i.e. the message was rejected, which prevents the rest of the delivery instructions from being followed), and an exit code of 111 indicates that the delivery failed temporarily and that delivery should be retried later (which also stops progress through the file). This allows for some interesting filtering possibilities:

# user2 never has anything good to say
| test $SENDER == [email protected] && exit 100
# user3 doesn't either, but he gets mad if I reject his emails
| test $SENDER == [email protected] && exit 99
# do I have enough space? # (this is unnecessary, and is just for demonstration) |quota|tail -1|awk '$4 < 90 { print yes }'|grep -q yes||exit 111
# if I make it this far, it's time to deliver my email
./Maildir/

Although .qmail-based filtering can be very useful, it can be somewhat awkward for complex filtering. It is often desirable to use a program with a more concise filtering language to filter and deliver mail. The two most common programs that serve this purpose are procmail and maildrop. A basic .qmail file for using procmail would look like the following:

|
preline procmail ./.procmailrc

Using maildrop would be very similar.

Supporting .forward Files

The Sendmail email server has a feature similar to the .qmail file, whereby users can specify that messages addressed to them are forwarded elsewhere. In particular, users can create a file named .forward in their home directory containing a forwarding email address. There is a program that provides some compatibility with this Sendmail feature called dot-forward (http://cr.yp.to/dot-forward.html). The dot-forward program is used in .qmail files or, more usefully, in the default delivery instructions given to qmail-start. The dot-forward program does not support the full range of syntax that a modern Sendmail installation allows in the .forward files, but does support the most basic syntax. A .qmail-style syntax that uses the dot-forward command is very simple.

| dot-forward

Users

Qmail uses a very flexible definition of a user. Like most other mail servers, one definition of user is operating-system-defined users. In other words, the users specified in the /etc/passwd file (or wherever the operating system stores user information). These users are used by default for the local domains. However, the operating-system-defined users are easily overridden.

A user, in the most general sense, is a unique delivery script associated with a unique email address. For example, [email protected], [email protected], and [email protected] are all different addresses, and probably refer to different accounts (users): fred, fran, and pat. The simplest case uses the operating system to define all these accounts.

As a category, virtual users are all users that are not defined by the host operating system, but are instead defined either by qmail or by some other program. Qmail has several different forms of virtual users:

  • Aliases

  • Qmail-defined, or mapped, users

  • Users specified in the control/virtualdomains file

  • Extensions to existing users

When attempting to deliver a message, qmail decides how to deliver the message by performing the following operations in the order listed:

  1. Check control/virtualdomains

  2. Check for qmail-defined users

  3. Check for operating-system users

  4. Check for aliases

During delivery, qmail handles any address extensions.

The virtualdomains File

The first location that can define a user is the control/virtualdomains file. This file is explained in greater detail in Chapter 5, however, to explain it in short, email addresses defined in this file are rewritten to map to another user. The email then continues through the sequence of user lookups to be delivered with this new destination.

Defined Users: The users/assign File

The users/assign file is a very powerful tool for controlling delivery rules. It is used to define users specific to qmail, to map these users to other users, and even to change the rules defining extension addresses. This file is compiled into a CDB file to make it extremely quick to look up delivery instructions. For this reason, systems with large numbers of operating-system-defined users frequently put them here to increase the speed of the delivery process by locating the user's home directory and UID/GID faster. Qmail provides a tool for copying users from /etc/passwd to users/assign, to make this process easy. The tool, qmail-pw2u, reads a SysV7-style /etc/passwd file from standard input and prints users/assign entries for each user to its standard output.

Aliases

After checking the operating-system-defined user list (/etc/passwd), if no matching user is found, the aliases are consulted. Aliases are defined by .qmail files in the alias user's home directory. For example, creating a file in the alias user's home directory named .qmail-someuser defines an alias for the address [email protected]. Another example is the root alias that was defined in Chapter 1. A file named .qmail-root in ~alias/ establishes the address [email protected]. When attempting to deliver messages addressed to [email protected], qmail first checks the list of users (in /etc/passwd). Because it ignores all users whose UID is zero—namely, root—it doesn't find a match. Next qmail consults the aliases defined in the home directory of the alias user. Upon finding the ~alias/.qmail-root file, it delivers the message according to the instructions contained therein.

Extensions

One of the more unusual features of qmail email delivery is the address extension feature. In a default qmail configuration, the local part of an email address (considering that email addresses are in the form local@host) can contain, in addition to an account name, extra information called an extension separated from the account name by a hyphen. Extensions are defined by the recipient user with .qmail files in that user's home directory.

For example, messages addressed to [email protected] will be delivered to the user named user. A message addressed to [email protected] will be delivered according to the extensions that user has defined (presuming there is no user named user-extension). What extensions the user permits are defined by .qmail files in the user's home directory. Delivery instructions for messages addressed to [email protected] are listed in a .qmail file in user's home directory, specifically ~user/.qmail. Extensions are defined by adding the extension to the name of the .qmail file. For example, the file .qmail-foo will define the foo extension, enabling delivery of messages addressed to [email protected]. It is also possible to have wildcard matching, on a prefix-match-only basis. Specifically, the ending -default in a .qmail file name will match anything matching the existing prefix. Using the example of a message addressed to [email protected], qmail will look for the following files, in the order given:

  1. ~user/.qmail-foo

  2. ~user/.qmail-default

  3. ~alias/.qmail-user-foo

  4. ~alias/.qmail-user-default

  5. ~alias/.qmail-default

The first one that exists is read by qmail-local to get the delivery instructions. Extension resolution can be more complicated. For example, if a message is addressed to [email protected], qmail looks for the following files, in the order given:

  1. ~user/.qmail-foo-bar-baz

  2. ~user/.qmail-foo-bar-default

  3. ~user/.qmail-foo-default

  4. ~user/.qmail-default

  5. ~alias/.qmail-user-foo-bar-baz

  6. ~alias/.qmail-user-foo-bar-default

  7. ~alias/.qmail-user-foo-default

  8. ~alias/.qmail-user-default

  9. ~alias/.qmail-default

The first of these files that exists is used to deliver the message. If none of them exists, the message is rejected and the original sender is sent a bounce message saying that the message could not be delivered because the specified recipient does not exist. The user identification process—where the local part of the address, user-foo-bar-baz, is resolved to the user named user—is performed by qmail-lspawn. When qmail-local runs, the choice of which user will receive the message has already been made. It is qmail-local's task to identify the correct .qmail file for the address, given the recipient user identified by qmail-lspawn.

There are several environment variables that may be defined (listed in the Pipes and Programs section), depending on which .qmail file is used to deliver the message. For each of the above cases, if RECIPIENT is [email protected], LOCAL is user-foo-bar-baz, and USER is user, the relevant environment variables will be defined during delivery as follows:

Matching .qmail file

EXT

EXT2

EXT3

EXT4

DEFAULT

.qmail-foo-bar-baz

foo-bar-baz

bar-baz

baz

  

.qmail-foo-bar-default

foo-bar-baz

bar-baz

baz

 

baz

.qmail-foo-default

foo-bar-baz

bar-baz

baz

 

bar-baz

.qmail-default

foo-bar-baz

bar-baz

baz

 

foo-bar-baz

A common misconception is that emails sent to [email protected] will be delivered according to the .qmail-default file in user's home directory. Keep in mind that the -default only matches extensions, not lack of extensions. For the same reason, when delivering a message addressed to [email protected], qmail-local does not check for the file .qmail-foo-bar-baz-default.

Delivering Email Remotely

The qmail-rspawn program, similar to qmail-lspawn, is given commands to deliver messages. The difference is that the messages qmail-rspawn must deliver must be delivered remotely. The qmail-rspawn program merely hands the message to qmail-remote along with the host to be contacted, the envelope sender, and the envelope recipient. The qmail-remote instances are spawned asynchronously, so deliveries can happen in any order. Unlike qmail-lspawn, which performs user identification and sets up the environment for qmail-local, qmail-rspawn functions merely as a launcher for qmail-remote that reads messages from the queue.

While qmail-rspawn and qmail-remote both run as a user (qmailr) with permission to read mail out of the qmail queue, they behave similarly to qmail-lspawn and qmail-local. The qmail-rspawn program reads the message from the queue and feeds it to qmail-remote.

How It Normally Works

The delivery commands from qmail-send consist of only a message number, a sender address, and a recipient address. The qmail-rspawn program extracts the destination domain from the recipient address and gives all that information, unmodified, to qmail-remote. The qmail-remote program performs all of the necessary SMTP-defined behavior to deliver a message: it looks up the DNS MX records for the destination hostname (and if there are none, the DNS A records) to determine what IP address to contact, contacts that IP address, negotiates the delivery, and transmits the message.

Because qmail-send limits the number of concurrent deliveries, offline destinations can cause problems. When a given IP address cannot be contacted (for example, because it is offline), qmail-remote is required to wait for the usual timeout (configurable via control/timeoutconnect) before giving up. Similarly, if a destination goes offline while qmail-remote is communicating with it, qmail is required to wait for a different timeout (configurable via control/timeoutremote) before giving up. Because of this waiting process, it's possible that all of the allowable concurrency in remote deliveries could be used by qmail-remote instances attempting to contact the same offline server. In some cases (such as when the server first goes offline) this is unavoidable. After several failed attempts to contact a server such a situation is avoidable. qmail-remote keeps track of servers that it could not contact even after trying twice in two minutes (without intervening successful connections) and prevents further attempts for an hour. This record of offline servers can be cleared manually to force immediate retries by running the qmail-tcpok program as root.

Static Routes

It is sometimes necessary or desirable to avoid performing DNS lookups to determine which host to contact for a given destination domain. There are several reasons for this like speed (if DNS lookups are slow and the target IP will virtually never change), DNS information for that domain being unavailable, cases where messages must be relayed but where the public DNS information should not be used by this mail server, and so on. For example, if a qmail server is serving as a backup MX server for a given domain, obeying the DNS MX records is likely to produce bad behavior. These records might list additional backup servers, which would cause qmail to send these mails to the other backup servers causing the messages to loop until the primary MX becomes available. Instead, these messages should be sent only to the primary mail server, whenever it comes back online. By specifying the primary mail server for that domain in the control/smtproutes file, the correct behavior is achieved.

Static routes are specified in the file control/smtproutes, which is read by qmail-remote before doing DNS lookups. The format of this file is a series of entries, one per line, in one of the following two forms: domain:relay or domain:relay:port. In this form, domain is the domain that is being redirected and relay is the hostname (or square bracketed IP address) to deliver that domain's mail to. The port allows the specification of a port number to use other than the SMTP default port of 25. For example, if the file contains:

example.com:realdomain.com
example2.com:[1.2.3.4]
example3.com:anotherdomain.com:26

This means that if a message is addressed to [email protected], it is delivered to realdomain.com, as if example.com's only MX record specified realdomain.com. If a message is addressed to [email protected], it is delivered to the IP address 1.2.3.4, and if a message is addressed to [email protected], qmail-remote delivers the message to anotherdomain.com, but will contact that server on port 26 rather than the usual port 25.

It is possible to use prefix wildcards in this file by using a line that begins with a period. The longest match in the file will be used. For example:

.example.com:[1.2.3.4]
.com:[1.2.3.5]

This specifies that all domains ending in .example.com, such as foo.example.com and bar.example.com (but not badexample.com, or example.com) must be delivered to the IP address 1.2.3.4. Other messages whose destination ends in .com will be delivered to the IP address 1.2.3.5. Also, a catch-all entry may be specified by omitting the matching domain entirely. For example:

:mail.isp.com

This tells qmail-remote to deliver all messages, regardless of their destination, to the mail server of mail.isp.com. Additionally, the relay part of a line can be omitted, specifying that matching destinations should be delivered normally. This is useful for specifying exceptions, for example:

example.com:
:mail.isp.com

This specifies that all mail destined for example.com should be delivered according to the example.com DNS records, while all other mail should be relayed through the mail.isp.com email server. The routes listed in the file can have only one match. In other words, to direct example.com email somewhere, only one line (the first one found) may be used. Each route can only list a single hostname or IP address. If multiple potential destinations are required, keep in mind that hostnames may resolve to multiple IP addresses.

Authentication

When relaying messages through another server, in some circumstances it is useful to authenticate oneself to that other server. There are many ways of authenticating to a remote server, including authenticating with SSL certificates and authenticating with a username and password. Unfortunately, qmail does not support any such behavior natively.

This functionality can be added to qmail either with a patch written by Bjoern Kalkbrenner: (http://www.cyberphoria.org/?display=projects_qmail_smtp_auth_send_patch) or with a patch based on that written by Dr. Erwin Hoffmann (http://www.fehcom.de/qmail/auth/qmail-authentication-067_tgz.bin). Both patches introduce a new control file, control/smtproutes_users, whose syntax is similar to that of control/smtproutes. Entries are lines in the file of the form:

user@domain:relay|username|password

The most common use of this file is in relaying all messages to a server that requires authentication, such as a service provider's server. Similar to control/smtproutes, omitting the part before the first colon creates an entry that matches all messages. If all messages must be relayed through the mail.isp.com server, with the username myaccount and the password bigsecret, the control/smtproutes_users entry would be:

:mail.isp.com|myaccount|bigsecret

Summary

This chapter has explained both primary ways in which email exits qmail's queue and is delivered. All of the major facets of delivery were covered, including what happens when delivery fails, how delivery can be filtered with .qmail files, how users are defined, and how to control remote delivery. The next chapter will cover local delivery in greater detail, focusing on storage formats and popular methods of accessing email.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.160.216