SMTP 101: Sending Emails
In this post, we're going to dive into a simple question that contains a lot of complexity: how does my email client send emails? In a previous post, I explained how emails are retrieved, featuring a protocol called IMAP. Today, we're exploring something called SMTP (Simple Mail Transfer Protocol), which involves sending emails.
SMTP is one of the all-time classic Internet protocols, dating back to 1980. Beyond just sending emails, SMTP can teach you a lot about the workings of the Internet!
Exploration
Our journey starts when you (you@icloud.com
) are trying to send an email to
your friend, friend@gmail.com
. It might seem like this is a decentralized
process: that is, you can directly send your email to their machine, somehow.
For better and for worse, that's no longer the way to do things.1
Instead, you need to pass off your email to a centralized email server,
which will relay your message to friend@gmail.com
.
The server will take care of sending it to your friend, for you! It's a lot like a post office.
Connecting
The next thing we need to figure out is the location of this helpful email
server, so we can connect to it and give it our email. The key is in our email
address: @icloud.com
. This means that iCloud Mail manages the server, so we
need to give our email to an iCloud mail server in order for it to be sent to
our friend.
Apple Support tells us that the
FQDN and port of
the iCloud Mail email server is smtp.mail.me.com:587
.2
For this demo, I'll use the OpenSSL command-line tool to connect to the server:
$ openssl s_client -connect smtp.mail.me.com:587 -starttls smtp -quiet -crlf
# Output about the connection
There are some arguments here that you might be unfamiliar with. The most
significant one, though, is -starttls smtp
.3 To understand this, we'll
need to dive into implicit vs. opportunistic TLS.
TLS Stuff
There are two ways to obtain a TLS connection:
- Opportunistically
- Implicitly
A server that supports opportunistic TLS upholds that the connection it starts with is cleartext (not secure), but can be upgraded to a TLS connection using a special command. This command varies depending on the protocol.
Some servers don't care at all about cleartext connections, though, so they use implicit TLS. This means that the TLS handshake is attempted when the connection begins (not upon user request).4
Getting back to the -starttls smtp
argument, it tells openssl
that we want
to write the STARTTLS
SMTP command to the server, which is by request (it does
not happen implicitly). So the server located at smtp.mail.me.com
accepts
opportunistic TLS.5
SMTP
With all that out of the way, we can start talking about SMTP! SMTP is the protocol used to communicate the information of your email (i.e. contents, recipient, CC, BCC, etc.) to the server. We send SMTP commands that detail this information.
Now that we're connected, the first thing we need to do is greet the server:
C: EHLO dzfrias.local
250-PIPELINING
250-SIZE 28319744
250-ETRN
250-AUTH LOGIN PLAIN ATOKEN ATOKEN2 WSTOKEN WETOKEN XOAUTH2
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250-SMTPUTF8
Lines beginning with C:
denote my input, and lines without C:
are server
output. EHLO
is the greeting command, and you also pass the name of the
computer you're connecting from along with it (dzfrias.local
for me).6
Authorization
Now, we want to identify ourselves as you@icloud.com
, or else the server won't
let us send any emails. The 250-AUTH
line of the greeting response tells us
what authentication methods we can use to log in. The one that we'll be using
for this demo is PLAIN
, which is the simplest, and only involves sending our
username and password:7
C: AUTH PLAIN
334
C: AHlvdUBpY2xvdWQuY29tAGVtYWlsLW1lLXlvdXItZmF2b3JpdGUtY29sb3I=
235 2.7.0 Authentication successful
That seemginly random string of characters is actually super important! I'll go through what happened line by line:
- I told the server I wanted to authenticate using the
PLAIN
method - The server said: “okay!”
- I gave it the base64 encoding of my username and password
- The server said: “looks good!”
That's enough to get us authenticated, so let's get to sending the email.
Sending Our Email
We can use different SMTP commands to tell the server about the email we want to send. For example:
C: MAIL FROM:<you@icloud.com>
250 2.1.0 0k
This tells the server that we're sending from you@icloud.com
.8 We
can also tell the server who the email is to and what it contains, using
RCPT TO
and DATA
, respectively:
C: RCPT TO:<friend@gmail.com>
250 2.1.5 0k
C: DATA
C: From: "You" <you@icloud.com>
C: To: "Friend" <friend@gmail.com>
C:
C: This is an extremely important message.
C: .
250 2.0.0 0k: queued as 343FD5003CE
Email sent! Note the two MIME headers we included in the content of the email. These contain crucial metadata that'll be packaged with our message.
You might be wondering what the .
is at the end. After we send the DATA
command, the server gives us the ability to write as many lines as we want,
which is necessary to input a multi-line email. Since line breaks usually
denote the end of an input, the server needs some other indicator for when our
input has ended. So instead, the server looks for a single dot on an empty line
to terminate our input. This technique is called dot-stuffing.
Summary
Here's a quick, high-level summary of this demo:
- Sending emails involves passing off your message to an email server to be relayed
- The email server corresponds to your email provider
- The SMTP protocol is used to communicate with the server
I'll continue my email-client series in future posts, but I'll mostly focus on the implementation-side of things for a client. For now, I hope you enjoyed this post. If you have a problems or suggestions, feel free to send me an email or open an issue on this website's GitHub repository.
References
These sources were super helpful for understanding these concepts, so I recommend reading them to deepen your understanding of the things I touched on in this post.
There are some advantages to this: in order to send emails directly, the receiver's machine would have to be listening for connections 24/7. It'd also be much harder to access emails from two different computers, since directly sending would imply that the emails are stored locally. ↩︎
Note that this will change depending on the email provider. Gmail, for example, uses
smtp.gmail.com:587
. ↩︎All arguments are crucial, though!
-crlf
will make sure that any newlines we send are turned into CRLF characters.-quiet
not only makes the output less verbose, but it also disables the interactive interpretation of the R and Q characters when typing intoopenssl
. I don't fully understand what that means, but this StackOverflow post explains it. Not passing it in will cause problems later. ↩︎The nuances of TLS handshakes is beyond the scope of this post. Definitely future-post material. For now, just know that a TLS handshake is essential for upgrading a cleartext connection to a TLS connection. ↩︎
If you read my IMAP post and are wondering why we didn't pass
-starttls smtp
to the IMAP server, it's becauseimap.mail.me.com:993
supports implicit TLS connection. In general, the adoption of implicit TLS SMTP servers has been slow. ↩︎The greeting command used to be
HELO
, but it's since been deprecated in favor ofEHLO
(short for extended-hello). ↩︎I encourage you to research the other authentication methods!
LOGIN
, for example, is very similar toPLAIN
, but it's no longer recommended. ↩︎If you try using an email address other than the one you authenticated in with, you'll get an error. No email spoofing allowed! ↩︎