D. J. Bernstein
Internet mail
SMTP: Simple Mail Transfer Protocol

How the client transfers mail

Here is how a typical SMTP connection works.

The client has responsibility for delivering a message to one or more envelope recipients, or notifying an envelope sender if delivery is not made. It wants to transfer this responsibility to the server.

The client waits for the server's greeting:

     220 heaven.af.mil ESMTP
If the server does not accept the connection, the client quits.

The client then sends a HELO request, and waits for the response:

     HELO sun.af.mil
     250 heaven.af.mil
If the server does not accept the request, the client quits.

The client then sends a MAIL request showing the envelope sender address, and waits for the response:

     MAIL FROM:<driver@sun.af.mil>
     250 ok
If the server does not accept the request, the client quits.

The client then sends one RCPT request for each envelope recipient address, waiting for a response after each address:

     RCPT TO:<God@heaven.af.mil>
     250 ok
It keeps track of which addresses are accepted. If none of the addresses are accepted, the client quits.

The client then sends a DATA request, and waits for the response:

     DATA
     354 go ahead
If the server does not accept the request, the client quits.

The client then sends the encoded message, waits for the response, and quits:

     Date: 8 Aug 1998 04:10:45 -0000
     From: driver@sun.af.mil
     To: God@heaven.af.mil
     Subject: chariot is back in service

     I patched up the wheel. It's as good as new.
     .
     250 ok 902549473 qp 24035
     QUIT
     221 heaven.af.mil
If the client encounters a message I/O error, it closes the connection immediately, without sending a final dot and without sending a QUIT request.

Success, failure, deferral

For each recipient, the client decides whether responsibility was transferred to the server. There are three possible results: This result is determined by the first digit of the server's response codes for the connection (C), the HELO request (H), the MAIL request (M), the RCPT request for this recipient (R), the DATA request (D), and the encoded message (E), according to the following table:
     C H M R D E result
     --------------------
     5           deferral (some clients: failure)
     4           deferral
     2 5         deferral (some clients: failure)
     2 4         deferral
     2 2 5       failure (a few clients: deferral)
     2 2 4       deferral
     2 2 2 5     failure
     2 2 2 4     deferral
     2 2 2 2 5   failure
     2 2 2 2 4   deferral
     2 2 2 2 3 5 failure
     2 2 2 2 3 4 deferral
     2 2 2 2 3 2 success

Closed connections and message duplication

The connection may close while the client is waiting for a response: e.g., the server may crash, or the network may fail. The client acts as if all subsequent responses have codes beginning with 4.

In particular, say the client sends the dot at the end of the encoded message, the server accepts the message, and the connection closes before the client receives the server's response. The client does not know that the server accepted the message; it will try again later. Both the client and the server now have responsibility for the delivery. The recipient will end up receiving two copies of the message.

To minimize the chance of this event, high-quality servers respond as quickly as possible to encoded messages.

Sending more than one message

Sometimes a client has more than one message to transfer to the same server. Instead of quitting, it can send an RSET request to clear the server's envelope, and then continue with a new MAIL, RCPT, etc.

If the second message has the same contents and the same envelope sender as the first message, the client can save time by merging the recipient lists and sending the message just once. Exception: if the new recipient list is longer than the server can handle, the server will end up deferring some recipients.

I recommend that clients avoid holding connections open when they do not have messages to send immediately. An idle connection chews up memory on the server that could be used for other tasks.