Skip to content

fix: send the SMTP greeting line and its CRLF in a single write#3599

Open
flowww-cicd wants to merge 1 commit into
postalserver:mainfrom
flowww-cicd:fix/smtp-greeting-single-write
Open

fix: send the SMTP greeting line and its CRLF in a single write#3599
flowww-cicd wants to merge 1 commit into
postalserver:mainfrom
flowww-cicd:fix/smtp-greeting-single-write

Conversation

@flowww-cicd

Copy link
Copy Markdown

Summary

The initial 220 greeting is emitted with IO#print, which writes the greeting string and then the $\ output record separator ("\r\n", set in prepare_environment) as two separate writes. On the wire these can be flushed as two TCP segments: the greeting text, then a delayed CRLF.

Impact

Strict clients that read the greeting text before its CRLF arrives may issue EHLO early; the trailing CRLF then arrives prepended to the EHLO reply and breaks their line parsing. Observed in production with Persits AspEmail.NET: the client keeps waiting for a "complete" greeting line, hangs until timeout, and never issues STARTTLS/AUTH — so no mail is ever sent.

TCP is a byte stream (RFC 5321 §2.1), so splitting a response across segments is legal server behaviour and such clients are arguably at fault. But every other response in the SMTP server is already written as a single line; making the greeting consistent (one atomic write, CRLF included) sidesteps the issue at zero cost.

Change

app/lib/smtp_server/server.rb: replace new_io.print("220 … ESMTP Postal/#{client.trace_id}") (string + $\) with new_io.write("220 … ESMTP Postal/#{client.trace_id}\r\n").

The bytes on the wire are identical (the previous print appended the same "\r\n" via $\); they are now guaranteed to be written together. No behaviour change for compliant clients, so existing specs are unaffected.

The initial 220 greeting was emitted with IO#print, which writes the string
and then the $\ output record separator ("\r\n", set in prepare_environment)
as two separate writes. These can leave the socket as two TCP segments: the
greeting text, then a delayed CRLF.

Strict clients that read the greeting text before its CRLF arrives may issue
EHLO early; the trailing CRLF then arrives prepended to the EHLO reply and
breaks their line parsing (observed: the client waits for a "complete" reply
and hangs until timeout, never issuing STARTTLS/AUTH). TCP is a byte stream
(RFC 5321 2.1), so this is legal server behaviour, but writing the greeting
line atomically (matching how all other responses are written) avoids
tripping such clients.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants