smtpclient: handle server closing connection after writing its response to RCPT TO

if icloud.com has your ip blocklisted, it will close the smtp connection after
writing a response to RCPT TO, before writing a response to a pipelined DATA
command. this is similar to the case (already handled) where a mail server
would close the connection after a response to MAIL FROM when pipelined.

we now recognize this situation (unexpected EOF before we get a response to
DATA, with all RCPT TO's failed), and treat the last response to RCPT TO as the
result.

for issue #198 by soheilpro, thanks for reporting and sending an smtpclient
trace that showed the behaviour.
This commit is contained in:
Mechiel Lukkien
2024-08-22 21:59:53 +02:00
parent c16162eebc
commit 17346d6def
2 changed files with 36 additions and 0 deletions

View File

@ -1265,6 +1265,15 @@ func (c *Client) DeliverMultiple(ctx context.Context, mailFrom string, rcptTo []
c.xbotchf(0, "", "", nil, "writing pipelined mail/rcpt/data: %w", writeerr)
}
// If remote closed the connection before writing a DATA response, and the RCPT
// TO's failed (e.g. after deciding we're on a blocklist), use the last response
// for a rcptto as result.
if dataerr != nil && errors.Is(dataerr, io.ErrUnexpectedEOF) && nok == 0 {
c.botched = true
r := rcptResps[len(rcptResps)-1]
c.xerrorf(r.Permanent, r.Code, r.Secode, r.Line, r.MoreLines, "%w: server closed connection just before responding to data command", ErrStatus)
}
// If the data command had an i/o or protocol error, it's also a failure for the
// entire transaction.
if dataerr != nil {