Tyler Montney Tyler Montney - 4 months ago 32
Vb.net Question

How to properly accept a SMTP message

I want to set up a simple SMTP server, and I'm using Net.Mail.SmtpClient to send messages. Everything works well except the next time you send a message. As soon as the server accepts the TCP client, and then the the SMTP client throws an error. It says the server committed a protocol violation the response was OK. The exception doesn't stop the message from going through. It's just not clean and I'd like to understand why it happens.

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim smtpMail As New Net.Mail.SmtpClient
Dim msg As New Net.Mail.MailMessage
With smtpMail
.UseDefaultCredentials = False
.Port = 25
.EnableSsl = False
.Host = "127.0.0.1"
End With
With msg
.From = New MailAddress("me@test.com")
.To.Add("lookup@test.com")
.Subject = "Subject"
.Body = "Body"
End With
smtpMail.Send(msg)
End Sub


My server goes through after the above Sub is run:


  1. EHLO ("250 OK")

  2. MAIL ("250 OK")

  3. RCPT ("250 OK" upon user found.)

  4. DATA ("354 Start mail input; end with .")

  5. MIME ("250 OK")

  6. BODY ("250 OK")



After the BODY command, I get no further responses.

While True

Dim handler As TcpClient = _Listener.AcceptTcpClient()


Dim smtpReady() As Byte = Encoding.ASCII.GetBytes("220 Test SMTP Service ready" & vbCrLf)
handler.GetStream().Write(smtpReady, 0, smtpReady.Length)
Dim Message As String = String.Empty
Try
While True
bytes = New Byte(1024) {}
Dim bytesRec As Integer = handler.GetStream().Read(bytes, 0, bytes.Length)
dta = Encoding.ASCII.GetString(bytes, 0, bytesRec)

Dim CMD As String = dta.Substring(0, 4).ToUpper
Dim ReturnCMD() As Byte = Nothing
Select Case CMD
Case HELLO
ReturnCMD = Encoding.ASCII.GetBytes("250 OK" & vbCrLf)
handler.GetStream().Write(ReturnCMD, 0, ReturnCMD.Length)
Case MAIL
ReturnCMD = Encoding.ASCII.GetBytes("250 OK" & vbCrLf)
handler.GetStream().Write(ReturnCMD, 0, ReturnCMD.Length)
Case RECIPIENT
Dim SentTo As String = dta.Substring(8).Trim
If SentTo.ToLower <> "<lookup@test.com>" Then
ReturnCMD = Encoding.ASCII.GetBytes("550 No such user here" & vbCrLf)
handler.GetStream().Write(ReturnCMD, 0, ReturnCMD.Length)
Else
ReturnCMD = Encoding.ASCII.GetBytes("250 OK" & vbCrLf)
handler.GetStream().Write(ReturnCMD, 0, ReturnCMD.Length)
End If
Case data
ReturnCMD = Encoding.ASCII.GetBytes("354 Start mail input; end with ." & vbCrLf)
handler.GetStream().Write(ReturnCMD, 0, ReturnCMD.Length)
Case QUIT
ReturnCMD = Encoding.ASCII.GetBytes("221 Service closing transmission channel" & vbCrLf)
handler.GetStream().Write(ReturnCMD, 0, ReturnCMD.Length)
Exit While
Case RESET
ReturnCMD = Encoding.ASCII.GetBytes("221 Service closing transmission channel" & vbCrLf)
handler.GetStream().Write(ReturnCMD, 0, ReturnCMD.Length)
Exit While
Case EHELLO
ReturnCMD = Encoding.ASCII.GetBytes("250 OK" & vbCrLf)
handler.GetStream().Write(ReturnCMD, 0, ReturnCMD.Length)
Case MIME
ReturnCMD = Encoding.ASCII.GetBytes("250 OK" & vbCrLf)
handler.GetStream().Write(ReturnCMD, 0, ReturnCMD.Length)
Case BODY
Message += dta
RaiseEvent DataReceived(Message)
ReturnCMD = Encoding.ASCII.GetBytes("250 OK" & vbCrLf)
handler.GetStream().Write(ReturnCMD, 0, ReturnCMD.Length)
End Select

End While

handler.Close()
Catch ex As Exception
End Try
End While


The code probably has some redundancy/is messy. So far, this is just a test for something else. I'm just trying to get a handle on SMTP.

Answer

Since closing the connection seemed to be the problem, adding a Using block around your SMTP client might be useful. Using acts like a try-catch and safely closes and disposes of any resource connection when it hits the closing End Using -

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Using smtpMail As New Net.Mail.SmtpClient
        Using msg As New Net.Mail.MailMessage
            With smtpMail
                .UseDefaultCredentials = False
                .Port = 25
                .EnableSsl = False
                .Host = "127.0.0.1"
            End With
            With msg
                .From = New MailAddress("me@test.com")
                .To.Add("lookup@test.com")
                .Subject = "Subject"
                .Body = "Body"
            End With
            smtpMail.Send(msg)
        End Using
    End Using
End Sub