Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
In .NET 2.0 there is a cool class called SmtpClient within System.Net.Mail.
With this class you can send mail (at least I thought).
The requirement was to send an email over a server of one of our agencies.
The mail server was secured by username/password authentication. That's why I used the following code:
SmtpClient _client = new SmtpClient();
_client = new SmtpClient("smtp.myserver.at");
_client.UseDefaultCredentials = false;
_client.Credentials = new NetworkCredential("username", "password");
_client.Send("from@test.at", "to@test.at", "Hallo Welt", "Hallo max!!");
At execution of the last statement an exceptÃon occurred:
The inner exception said "Invalid lenght for a Base-64 char array."
That didn't make any sense for me (nor helped me to fix the problem).
I had a look at the stack trace and found out, that the exception had occurred in a class called
SmtpNtlmAuthenticationModule.
So I used telnet to manually connect to the smtp server.
S: 220 smtp.myserver.at ESMTP
C: ehlo asd.com
S: 250-smtp.myserver.at Hello mypc.microsoft.com [xxx.xxx.xxx.xxx]
S: 250-SIZE 20971520
S: 250-PIPELINING
S: 250-AUTH PLAIN LOGIN CRAM-MD5 NTLM
C: AUTH NTLM <base64 encoded string>
S: 334 NTLM supported
Seemed like the server would support NTLM.. But.. when I looked up the NTLM-SMTP specification, I found out that the server should respond with
334 <NTLM supported as base64 encoded string>
So the problem obviously is, that "NTLM supported" was not a valid Base-64 encoded string (as the inner exception above also pointed out).
So how could this problem be solved...
I digged into the private members of the SmtpClient object and found a member called transport (of type SmtpTransport).
The SmtpTransport object had private members as well, and one of them was called authenticationModules - bingo!
This is an array of ISmtpAuthenticationModules like Negotiate, NTLM, Digest and Login.
Unfortunately SmtpClient always picks the most effective supported method (in this case NTLM). As NTLM was not working I needed a way to kick out NTLM of the list of supported auth methods.
So I used reflection to modify the array and "disable" (override) NTLM in the array. Here's what I did:
FieldInfo transport = _client.GetType().GetField("transport",
BindingFlags.NonPublic | BindingFlags.Instance);
FieldInfo authModules = transport.GetValue(_client).GetType()
.GetField("authenticationModules",
BindingFlags.NonPublic | BindingFlags.Instance);
Array modulesArray = authModules.GetValue(transport.GetValue(_client)) as Array;
modulesArray.SetValue(modulesArray.GetValue(2), 0);
modulesArray.SetValue(modulesArray.GetValue(2), 1);
modulesArray.SetValue(modulesArray.GetValue(2), 3);
Guess which smtp authentication module will be used now :-)
Comments
Anonymous
April 16, 2008
PingBack from http://microsoftnews.askpcdoc.com/?p=2810Anonymous
April 16, 2008
This is how I worked it. Best regards, Sean Gahan Public Sub SendMail(ByVal emailTo As String, ByVal emailSubject As String, _ ByVal emailBody As String, ByVal nameOfFileOut As String, _ ByVal stringAttachment As String) Dim a As String = String.Empty '//attachment Dim f As String = AppSettings("SendFewrMailFromThisAcct") Dim b As String = String.Empty '//body Dim s As String = String.Empty '//subject Dim t As String = String.Empty '//to Dim att As Attachment Dim MailServer As String = AppSettings("MailServer") Dim MailUser As String = AppSettings("MailUser") Dim MailPwd As String = AppSettings("MailPwd").ToString Dim MailUserDomain As String = AppSettings("MailUserDomain") Dim memStream As System.IO.MemoryStream Dim encoding As New Text.ASCIIEncoding memStream = New System.IO.MemoryStream Dim byteArray As Byte() = encoding.GetBytes(stringAttachment) Dim sm As New SmtpClient(MailServer) Dim SMTPUserInfo As System.Net.NetworkCredential = _ New System.Net.NetworkCredential(MailUser, MailPwd, MailUserDomain) '//set up email stuff t = emailTo s = emailSubject b = emailBody Dim m As New MailMessage(f, t, s, b) Try If nameOfFileOut <> String.Empty Then memStream.Write(byteArray, 0, byteArray.Length) memStream.Seek(0, IO.SeekOrigin.Begin) att = New Attachment(memStream, nameOfFileOut, MediaTypeNames.Text.Plain) m.Attachments.Add(att) End If m.Priority = MailPriority.Normal sm.UseDefaultCredentials = False sm.Credentials = SMTPUserInfo sm.Send(m) Catch ex As Exception Throw New ApplicationException("Error: Message:" & ex.Message & vbCrLf & "Stack:" & ex.StackTrace) Finally memStream.Dispose() If Not att Is Nothing Then att.Dispose() End If m.Dispose() sm = Nothing End TryAnonymous
April 16, 2008
Hi knom, Thanks for the useful article, I'm having a similar problem. I think I need to use this method to force the use of the "login" authenticationModule. How would I do this? PhilAnonymous
June 22, 2008
I have the same code but I get "The operation has timed out."Anonymous
September 23, 2008
Hugely helpful - I had been puzzling over this for ages. Actually I also needed the "login" authentication Module so I replaced ONLY the ntlm authentication module : modulesArray.SetValue(modulesArray.GetValue(2), 1) Worked like a dream!Anonymous
March 30, 2009
The comment has been removed