Whenever we’re working with someone to diagnose some obscure delivery issue one of the things we usually have them try is to “run a transaction by hand”. Being able to do that is a trick that everyone working with email should be able to do. I was drafting a blog post today and wanted to refer to running a transaction by hand and I realized that we hadn’t actually explained it anywhere. So here we are.
When you’re running a transaction by hand you’re doing everything your mailserver would do to deliver an email, but you’re doing it yourself. That means that you get to see all the responses from the mailserver you’re sending the mail to, and also any delays or errors in much more detail than you can usually get from mailserver delivery logs.
I want to send some email to firstname.lastname@example.org. There are two main steps to doing this – first I need to find out which mailserver I need to talk to to send mail to gmail, then I need to actually send the mail.
To find the mailserver I have to look up the MX record for gmail.com. From a unix / linux / mac command prompt you can do that like this (the bits you type are in orange):
platter:~ steve$ host -t mx gmail.com gmail.com mail is handled by 5 gmail-smtp-in.l.google.com. gmail.com mail is handled by 30 alt3.gmail-smtp-in.l.google.com. gmail.com mail is handled by 10 alt1.gmail-smtp-in.l.google.com. gmail.com mail is handled by 40 alt4.gmail-smtp-in.l.google.com. gmail.com mail is handled by 20 alt2.gmail-smtp-in.l.google.com.
However you do the DNS query you’ll usually end up with one or more hostnames, each with an associated priority. When you’re sending email you’re supposed to try those servers in order of priority, lowest number first, so in this case we’re going to be talking to the server gmail-smtp.in.l.google.com.
If you’re trying to replicate a delivery failure that you’ve seen then you can skip this step and go straight to trying to deliver to the same mailserver you saw fail previously (though double checking that that is a real MX for the domain is a good idea).
You may sometimes not get any answers to the MX record lookup – if, and only if, that happens then you should send mail directly to the domain part of the email address (“gmail.com” in this example).
Now we know which mailserver we’re going to talk to, so we connect to it with telnet on port 25. You can do this from a unix / linux / mac command line, or from a Windows command prompt (though a Windows firewall may be configured to block you from doing this). Again, the bits you type are in orange, while the responses from the server are in blue
platter:~ steve$ telnet gmail-smtp-in.l.google.com 25 Trying 184.108.40.206... Connected to gmail-smtp-in.l.google.com. Escape character is '^]'. 220 mx.google.com ESMTP y27si6889009wfi.2 HELO platter.wordtothewise.com 250 mx.google.com at your service MAIL FROM:<email@example.com> 250 2.1.0 OK y27si6889009wfi.2 RCPT TO:<firstname.lastname@example.org> 250 2.1.5 OK y27si6889009wfi.2 DATA 354 Go ahead y27si6889009wfi.2 From: <email@example.com> To: <firstname.lastname@example.org> Subject: Just a test email The body of the mail goes here. . 250 2.0.0 OK 1276638161 y27si6889009wfi.2 QUIT 221 2.0.0 closing connection y27si6889009wfi.2 Connection closed by foreign host.
A simple mail delivery like this goes through a number of steps. First you telnet to the server on port 25, wait for that to connect, then wait for the server to send you it’s “banner” (“220 mx.google.com ESMTP …”). Then you tell the server who you are with the “HELO” command, and wait for a response.
Then you tell it the email address you’re sending mail from with the “MAIL FROM” command – note that there’s a colon after MAIL FROM, and then your bare email address surrounded by angle brackets – and wait for a response. Next you tell it who you want to send mail to, with the RCPT TO command – again followed by a colon and the recipients bare email address surrounded by angle brackets.
(You can send mail to multiple recipients by repeating the RCPT TO command here).
Then you’re ready to send the email itself, so you send the “DATA” command to tell the server you’re about to do that, and wait for it to respond with something like “354 Go ahead”. Then you enter the email you want to send, consisting of the headers, a blank line, then the body of the message. To tell the server you’re done, send a period (“.”) on it’s own as the final line. You’ll typically want to have at least To:, From: and Subject: headers and a short message body for even a simple test, but if you’re considering content-related filtering then you’ll want to paste in a copy of a real email.
Once the server has accepted the email you send the command “QUIT” to tell it you’re done, and it will close the connection.
You can see that each response from the server starts with a three digit number. If that starts with a “2″ it means that it’s happy with what you just told it, if it starts with a “3″ it’s waiting for you to send the text of the email. If it starts with a “4″ or a “5″ it means it’s unhappy with what you just told it – maybe you typoed something, maybe it’s rejecting the mail delivery, maybe you tried commands in the wrong order – and the text after the number should give you some idea as to why.
If you typo something while you’re using telnet you can use backspace to delete and correct it while you’re on the same line. Once you’ve hit return, though, it’s already sent to the server and you can’t change it (and you might need to quite out and start over).
If you get confused at any point and need to quit out of telnet and start over you can use Ctrl-] (hold the ctrl key and press the close square bracket key) to get to a telnet> prompt, then type “quit”.
There’s some more sophisticated things you can do, but they’ll have to wait for another post.
Meanwhile, feel free to use email@example.com to play with running an email transaction by hand.