A brief tutorial on the SMTP protocol

The Simple Mail Transfer Protocol (SMTP) is, as its name suggests, not particularly complicated. You can easily work your way through a typical SMTP session by hand using the command line telnet client. Telnet is installed by default on most Linux distributions and macOS, but is disabled in recent versions of Windows. If you’re a Windows user, you’ll need to follow the steps documented here to resurrect the telnet client before continuing: https://www.rootusers.com/how-to-enable-the-telnet-client-in-windows-10/

In this tutorial, we are going to use SMTP Bucket as our mail server, which is available at mail.smtpbucket.com and listens on port 8025. SMTP Bucket is a fake SMTP Server that simply stores all the e-mails it receives in a database and makes them available through its website and REST API. Let’s see what happens when we establish a connection using telnet:

> telnet mail.smtpbucket.com 8025
Trying 212.71.250.90...
Connected to smtpbucket.com.
Escape character is '^]'.
220 mail.smtpbucket.com

The server responds with status code 220 and its own identity (mail.smtpbucket.com in this case). Throughout this tutorial, the SMTP server’s responses are shown in red. SMTP commands issued by the client (i.e. things you type into the telnet window) will be shown in blue. Next, the client (i.e. you) begins the session by issuing the ‘HELO’ command, to which the server replies with status code 250, which indicates the last command was successfully completed.

HELO
250 Hello

Well, actually, that’s not strictly true. Most mail servers support various extensions to the core SMTP protocol (most commonly for things like authentication). Clients typically request a list of supported extensions by issuing the non-standard ‘EHLO’ command instead of HELO. SMTP Bucket doesn’t currently support anything not offered by standard SMTP, so it doesn’t understand EHLO and will respond with status 502 (Not supported). That will trigger the client to fall back to standard SMTP and issue the HELO command (unless it has been configured to abandon the session if a required extension is not supported – e.g. many e-mail clients have a ‘Require Authentication’ option).

Anyway, once this simple handshake is complete, we can begin sending e-mail. That’s initiated with the ‘MAIL FROM’ command, which also includes the e-mail address of the sender:

MAIL FROM:<smtptutorial_sender@example.com>
250 Sender OK

Then we specify the recipient using ‘RCPT TO’:

RCPT TO:<smtptutorial_recipient@example.com>
250 Recipient OK

Now it’s time to send the message body. We begin this process with the ‘DATA’ command. Any subsequent lines are interpreted as e-mail data until we send a single ‘.’ on a line by itself:

DATA
354 Start mail input; end with <CRLF>.<CRLF>
Subject:Subject line goes here!

The message body goes here!
.
250 Message accepted

Hopefully the purpose of the ‘Subject’ header is self explanatory, but note the need for a double line break separating it from the actual e-mail text. The server then responds with a final 250 status code to indicate the message has been accepted. If this were a real SMTP Server and not a fake one, this e-mail would now be on its way to smtptutorial_recipient@example.com. We could continue sending additional e-mails in this session, but our work here is done, so we issue the ‘QUIT’ command to close the connection:

QUIT
Connection closed by foreign host.

As we’re using SMTP Bucket, we can now use its REST API to find the e-mail we just sent:

> curl -G https://api.smtpbucket.com/emails --data-urlencode "sender=smtptutorial_sender@example.com"
{
 "results" : [ {
 "id" : "ff8081815974a2a30159c824b0b00105",
 "sender" : "smtptutorial_sender@example.com",
 "recipients" : [ "smtptutorial_recipient@example.com" ],
 "subject" : "Subject line goes here!",
 "timeCreated" : 1485121564000
 } ]
}

We can see that our e-mail has been saved with ID ‘ff8081815974a2a30159c824b0b00105’, and the sender and recipient are as we provided in the original request. Now we can retrieve the full e-mail with another GET request using its allocated ID:

> curl https://api.smtpbucket.com/emails/ff8081815974a2a30159c824b0b00105
{
  "id" : "ff8081815974a2a30159c824b0b00105",
  "sender" : "smtptutorial_sender@example.com",
  "recipients" : [ "smtptutorial_recipient@example.com" ],
  "subject" : "Subject line goes here",
  "body" : "Subject:Subject line goes here\n\nThis is the message body\n",
  "timeCreated" :1485121564000
}

We hope this tutorial has been useful. If you’d like to learn more about the SMTP protocol, it is formally defined in RFC 2821, which is available here: https://www.ietf.org/rfc/rfc2821.txt