I have to send an email through an SMTP server. This SMTP server requires authentication. However, if I use the default authentication mechanism for .NET SmtpClient, authentication fails with the 504 Unrecognized Authentication Type .
When using PuTTy, I can successfully authenticate:
250 AUTH LOGIN PLAIN AUTH LOGIN 334 VXNlcm5hbWU6 Some base64 encoded username 334 UGFzc3dvcmQ6 Some base64 encoded password 235 Authentication Successful
SmtpClient takes a slightly different approach when the username is passed using the AUTH LOGIN command:
250 AUTH LOGIN PLAIN AUTH LOGIN Some base64 encoded username 504 Unrecognized Authentication MAIL FROM: ...
504 seems to be ignored because it sends the MAIL FROM command. Reading some sources on the internet , it is allowed to send a username using the AUTH LOGIN command:
However, there is another version of this behavior compatible with RFC, when the client initially sends the user ID using the AUTH LOGIN method
The problem is most likely on the SMTP server side, but I do not control it.
Is it possible to force SmtpClient to send commands separately?
Code example
using (var client = new SmtpClient(_configuration.Host, _configuration.Port)) { client.UseDefaultCredentials = false; client.ServicePoint.MaxIdleTime = 1; client.EnableSsl = false; client.Credentials = new NetworkCredential(_configuration.Username, _configuration.Password, _configuration.Domain); var from = new MailAddress(string.Format("{0}@{1}", _configuration.Sender, _configuration.Domain)); var to = new MailAddress(string.Format("{0}@{1}", to, _configuration.Domain)); var message = new MailMessage(from, to) { Body = body }; client.Send(message); }
DIY
Just for fun (and not for production) I tried to make my own message:
using (var connection = new TcpClient(_configuration.Host, _configuration.Port)) using (NetworkStream stream = connection.GetStream()) { string header = Read(stream); Reply(stream, "EHLO " + Environment.MachineName); while (!Read(stream).ToUpperInvariant().Contains("AUTH LOGIN PLAIN")) { } Reply(stream, "AUTH LOGIN"); string requestUsername = Read(stream); Reply(stream, Convert.ToBase64String(Encoding.ASCII.GetBytes(_configuration.Username))); string requestPassword = Read(stream); Reply(stream, Convert.ToBase64String(Encoding.ASCII.GetBytes(_configuration.Password))); string authSucces = Read(stream); Reply(stream, string.Format("MAIL FROM: <{0}@{1}>", _configuration.Sender, _configuration.Domain)); string senderOk = Read(stream); Reply(stream, string.Format("RCPT TO: <{0}@{1}>", to, _configuration.Domain)); string rcptOk = Read(stream); Reply(stream, "DATA"); string sendData = Read(stream); Reply(stream, body); Reply(stream, string.Empty); Reply(stream, "."); Reply(stream, string.Empty); stream.Close(); connection.Close(); } private string Read(NetworkStream stream) { var data = new byte[1024]; int received = stream.Read(data, 0, data.Length); string contents = Encoding.ASCII.GetString(data, 0, received); return contents; } private void Reply(NetworkStream stream, string reply) { reply = reply + Environment.NewLine; stream.Write(Encoding.ASCII.GetBytes(reply), 0, reply.Length); stream.Flush(); }
This works great. So itβs definitely about how SmtpClient and the actual Smtp server are exchanging.
Work around
It turns out that we talked to a system that itself implemented the SMTP protocol, and they did it wrong. Therefore, we decided to make the message ourselves until the supplier fixed its code. Fortunately, this is for internal purposes only, and communication is very predictable. We use a DIY code with some refactoring and cleanup.