Anatomy of an SMTP session
A real delivery, captured with swaks against Gmail's MX. Every server
response is verbatim; only the client IP is replaced. --quit-after RCPT stops
before DATA, so the handshake completes and no message is ever sent, which is how you test
a route without touching a recipient.
Reading the prefixes: === is swaks talking to you,
-> and <- are sent and received in the clear, and
~> and <~ are the same two inside TLS.
What each step is doing
Every line is a place delivery can fail, and most of the work is knowing which one it failed at. These are the ones worth stopping on.
Connect
=== Trying gmail-smtp-in.l.google.com:25...swaks resolves the MX and opens a TCP connection on port 25. A refusal or a timeout here is a network problem, not a mail problem, and it is the cheapest failure to misdiagnose.
220 greeting
<- 220 mx.google.com ESMTP 4fb4d7f45d1cf-6aa67d1108csi.33 - gsmtpThe receiver announces itself. Anything other than 220 and you never get to send: some receivers return a 421 here when they are shedding load, and a few return a 554 when the connecting IP is blocked outright.
EHLO
-> EHLO rastu.techYou announce who you claim to be. Receivers check this name against the reverse DNS of your connecting IP, and a mismatch costs reputation at several providers even though nothing rejects on it directly.
Capabilities
<- 250-mx.google.com at your service, [198.51.100.24]Everything after 250- is something the receiver supports. The last line uses 250 with a space rather than a hyphen, which is how you know the list ended. If STARTTLS is absent here, the session cannot be encrypted at all.
STARTTLS
-> STARTTLSUpgrades the connection already open rather than dialling a new one. This is the step MTA-STS and DANE exist to protect, because an attacker who can strip this line downgrades the whole session to cleartext.
The second EHLO
~> EHLO rastu.techNot a mistake. STARTTLS resets the session state, so the capability list has to be asked for again inside the encrypted channel, and it can legitimately differ from the first one.
MAIL FROM
~> MAIL FROM:<rastu@rastu.tech>The envelope sender, and the domain SPF is actually checked against. It is not the From: header the recipient sees, and the gap between those two is what DMARC alignment is about.
RCPT TO
~> RCPT TO:<postmaster@gmail.com>Where most rejections land: unknown user, mailbox full, recipient policy. A 550 here is about one address. A 550 at MAIL FROM or at connect is about you.
QUIT
<~ 221 2.0.0 closing connection 4fb4d7f45d1cf-6aa67d1108csi.33 - gsmtpBecause --quit-after RCPT stopped before DATA. The route is proven end to end and no message was ever transmitted.
When a line comes back wrong
A connection refused at the 220 is a different problem from a 550 at RCPT TO, which is a different problem again from a 250 at DATA followed by silence. That is the reason to read a clean session before reading a broken one: it tells you which question to ask.
The response reference covers what each response means, and the bounce classifier will tell you which action it needs.
Reproducing this
swaks is the tool worth having installed, and --quit-after RCPT is the flag
worth remembering: it proves a full route, including TLS and recipient acceptance, without
delivering anything to a real person.
swaks --to postmaster@gmail.com --from you@example.com \
--server gmail-smtp-in.l.google.com --ehlo example.com \
--tls --quit-after RCPT
Drop --quit-after RCPT and it delivers. Add --tlso instead of
--tls for implicit TLS on port 465, and -au / -ap
when you are testing a submission endpoint that requires authentication.