Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SocketException but emails are still sent #1875

Open
jovball opened this issue Jan 28, 2025 · 1 comment
Open

SocketException but emails are still sent #1875

jovball opened this issue Jan 28, 2025 · 1 comment

Comments

@jovball
Copy link

jovball commented Jan 28, 2025

This is almost certainly not a MailKit problem but I'm hoping you can point us in the right direction for either resolution or troubleshooting.

We are saving mail messages to a pickup folder, loading them and sending them out with MimeMessage/MailKit. This allows us to do additional logging of the messages/recipients and provides (in theory) an easy resend mechanism. We are batching them in groups of no more than 50 per SmtpClient connection but I get the same results when sending a single email per connection.

In the last month, we started having a lot of SocketException error on emails with attachments. These emails are typically 200K - 400K total size with the attachments. Here's the exception message we see.

System.Net.Sockets.SocketException (10060): A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.

We do not receive the 250 OK/QUIT/Bye response from the PostFix mail server and the logical assumption in our code is that this email has not been sent so we retry after a short interval. However, in most cases, it HAS been sent and this results in duplicate emails.

Here is the Smtp Protocol Log output for a single message with two exceptions that was actually sent three times although the lack of response and the SocketExceptions would imply that it was only sent once. I have snipped the MimeContent and redacted emails and other information.

# "Unsuccessful" email on attempt #1 with the socket exception. 

Connected to smtp://csmtp.domain.com:25/
S: 220 gmr01.domain.com ESMTP Postfix
C: EHLO APP-PROD-01
S: 250-gmr01.domain.com
S: 250-PIPELINING
S: 250-SIZE
S: 250-VRFY
S: 250-ETRN
S: 250-STARTTLS
S: 250-ENHANCEDSTATUSCODES
S: 250-8BITMIME
S: 250-DSN
S: 250 SMTPUTF8
C: MAIL FROM:<[email protected]>
C: RCPT TO:<[email protected]>
C: RCPT TO:<[email protected]>
C: RCPT TO:<[email protected]>
S: 250 2.1.0 Ok
S: 250 2.1.5 Ok
S: 250 2.1.5 Ok
S: 250 2.1.5 Ok
C: DATA
S: 354 End data with <CR><LF>.<CR><LF>
C: X-Unsent: 1
C: From: [email protected]
C: Subject: Annual Update Report
C:  40901
C: To: [email protected]
C: Cc: [email protected], [email protected]
C: MIME-Version: 1.0
C: Content-Type: multipart/mixed;
C:  boundary="--_=_NextPart41_8308578f-b902-43d0-ab72-1f9217c8898a"
C: 
C: This is a multi-part message in MIME format.
<snipped>
C: 
C: .


# "Unsuccessful" email on attempt #2 with the socket exception. 


Connected to smtp://csmtp.domain.com:25/
S: 220 gmr02.domain.com ESMTP Postfix
C: EHLO APP-PROD-01
S: 250-gmr02.domain.com
S: 250-PIPELINING
S: 250-SIZE
S: 250-VRFY
S: 250-ETRN
S: 250-STARTTLS
S: 250-ENHANCEDSTATUSCODES
S: 250-8BITMIME
S: 250-DSN
S: 250 SMTPUTF8
C: MAIL FROM:<[email protected]>
C: RCPT TO:<[email protected]>
C: RCPT TO:<[email protected]>
C: RCPT TO:<[email protected]>
S: 250 2.1.0 Ok
S: 250 2.1.5 Ok
S: 250 2.1.5 Ok
S: 250 2.1.5 Ok
C: DATA
S: 354 End data with <CR><LF>.<CR><LF>
C: X-Unsent: 1
C: From: [email protected]
C: Subject:  Annual Update Report
C:  40901
C: To: [email protected]
C: Cc: [email protected], [email protected]
C: MIME-Version: 1.0
C: Content-Type: multipart/mixed;
C:  boundary="--_=_NextPart41_8308578f-b902-43d0-ab72-1f9217c8898a"
C: 
C: This is a multi-part message in MIME format.
<snipped>
C: 
C: .


# Successful email on attempt #3

Connected to smtp://csmtp.domain.com:25/
S: 220 gmr01.domain.com ESMTP Postfix
C: EHLO APP-PROD-01
S: 250-gmr01.domain.com
S: 250-PIPELINING
S: 250-SIZE
S: 250-VRFY
S: 250-ETRN
S: 250-STARTTLS
S: 250-ENHANCEDSTATUSCODES
S: 250-8BITMIME
S: 250-DSN
S: 250 SMTPUTF8
C: MAIL FROM:<[email protected]>
C: RCPT TO:<[email protected]>
C: RCPT TO:<[email protected]>
C: RCPT TO:<[email protected]>
S: 250 2.1.0 Ok
S: 250 2.1.5 Ok
S: 250 2.1.5 Ok
S: 250 2.1.5 Ok
C: DATA
S: 354 End data with <CR><LF>.<CR><LF>
C: X-Unsent: 1
C: From: [email protected]
C: Subject:  Annual Update Report
C:  40901
C: To: [email protected]
C: Cc: [email protected], [email protected]
C: MIME-Version: 1.0
C: Content-Type: multipart/mixed;
C:  boundary="--_=_NextPart41_8308578f-b902-43d0-ab72-1f9217c8898a"
C: 
C: This is a multi-part message in MIME format.
<snipped>
C: 
C: .
S: 250 2.0.0 Ok: queued as E8785C004E6
C: QUIT
S: 221 2.0.0 Bye

Here is the code

 using (var client = new SmtpClient(new ProtocolLogger(smtpLogPath)))
    {
      client.Timeout = (int)TimeSpan.FromMinutes(3).TotalMilliseconds;
        var host = _smtpClientOptions.Server;
        var port = _smtpClientOptions.Port;
        var socketOptions = SecureSocketOptions.None;                
        await client.ConnectAsync(host, port, socketOptions);

        var hasException = true;
// send a batch of messages
        foreach (var m in messages)
        {
            var messageId = Path.GetFileNameWithoutExtension(m.FullName);
            var messageSize = m.ReadableFileSize();
            //assuming failure for logging purposes
            hasException = true;

            try
            {
                var message = MimeMessage.Load(m.FullName);
                var messageSubject = message.Subject;

                if (!client.IsConnected)
                {
                    hasException = false;
                    _logger.LogWarning("Abort sending message {MessageId} and any other staged messages because the client is not connected", messageId);
                    break;
                }

                _logger.LogInformation("Sending message {MessageId} with file size {ReadableFileSize} '{MessageSubject}' to {Recipients}",
                    messageId, messageSize, messageSubject, recipients);
                var smtpServerResponse = await client.SendAsync(message);
                // 2.0.0 Ok: queued as 7347B10001A2" is an example response from the Postfix SMTP server
                _logger.LogInformation("SMTP server response for message {MessageId}: {SmtpServerResponse}", messageId, smtpServerResponse);
                
                //no issues
                hasException = false;
            }
            catch (SmtpCommandException ex) 
            {
                switch (ex.ErrorCode)
                {
                    case SmtpErrorCode.RecipientNotAccepted:
                        _logger.LogError(ex, "SMTP command failure {StatusCode} sending message {MessageFile} with file size {ReadableFileSize} - Recipient not accepted: {Recipient}",
                            ex.StatusCode, m.FullName, messageSize, ex.Mailbox.Address);
                        break;
                    case SmtpErrorCode.SenderNotAccepted:
                        _logger.LogError(ex, "SMTP command failure {StatusCode} sending message {MessageFile} with file size {ReadableFileSize} - Sender not accepted: {Sender}",
                            ex.StatusCode, m.FullName, messageSize, ex.Mailbox.Address);
                        break;
                    case SmtpErrorCode.MessageNotAccepted:
                        _logger.LogError(ex, "SMTP command failure {StatusCode} sending message {MessageFile} with file size {ReadableFileSize} - Message not accepted",
                            ex.StatusCode, m.FullName, messageSize);
                        break;
                    default:
                        _logger.LogError(ex, "SMTP command failure {StatusCode} sending message {MessageFile} with file size {ReadableFileSize}}",
                            ex.StatusCode, m.FullName, messageSize);
                        break;
                }
            } 
            catch (SmtpProtocolException ex) 
            {
                _logger.LogError(ex, "Protocol error failure sending message {MessageFile} with file size {ReadableFileSize}",
                    m.FullName, messageSize);
            }
            catch (Exception ex)
            {                    
                _logger.LogError(ex, "Failure sending message {MessageFile} with file size {ReadableFileSize}",
                    m.FullName, messageSize);
            }
            finally
            {
                if(hasException)
                {
                    resultInfo.AddResultFailure("Failure sending message", $"Message Id: {messageId} - {m.FullName}");
                }
            } 
        }
    }
@jstedfast
Copy link
Owner

Have you tried checking the postfix server logs?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants