共用方式為


Error: “Object reference not set to an instance of an object” when sending out mails using System.Net.Mail via Exchange 2007

This was a fun issue to work on! Some background, the customer was using System.Net.Mail to send out mails via Exchange 2007 using port 25 and port 587, all was working fine and then some updates were installed and he was no longer able to send out mails via port 587. He started to get the following exception:

Exception:
Object reference not set to an instance of an object.
System.Net Error: at System.Net.Mail.SmtpConnection.GetConnection(String host, Int32 port)
at System.Net.Mail.SmtpTransport.GetConnection(String host, Int32 port)
at System.Net.Mail.SmtpClient.GetConnection()
at System.Net.Mail.SmtpClient.Send(MailMessage message)

The code was very simple:

 static void Main(string[] args)
        {
            SmtpClient _client = new SmtpClient();
            
            using (MailMessage _message = new MailMessage())
            {
                _message.Subject = "Test message";
                _message.Body = "Test message"; 

                _message.From = new MailAddress("support@xyz.com");
                _message.To.Add("akashb@xyz.com");

                _client.Send(_message);
            }
        }

The host, port and the credentials were being picked up from the App.Config:

<network host="EX-01.xyz.COM" port="587" userName="support@xyz.com" password="Password"/>

What we were also told was that if in the code the SmtpClient.TargetName Property was set to blank then everything worked fine! What is this property? This property gets or sets the Service Provider Name (SPN) to use for authentication when using extended protection.

We made sure we have the exact same setting on the Exchange 2007 Receive Connectors, updated the client with all the updates that were installed in the customer’s environment but we initially could not reproduce the issue. It did not matter if we set the TargetName to blank or not. We finally did reproduce the issue after I put the client machine where the code was running in the same domain as the Exchange Server and then setting the TargetName to blank did matter. Now the next step was to find out the update that caused the code to fail. We did get a hint from the SmtpClient.TargetName property and started to look for the patch that could cause the issue.

Luckily, the search did not last long and we were able to identify the patch that caused the failure!

Description of the rollup update for the .NET Framework 3.5 Service Pack 1 and the .NET Framework 2.0 Service Pack 2 on Windows XP and on Windows Server 2003 (976765 and 980773): June 8, 2010
https://support.microsoft.com/kb/982167

This updates the NLC on the system to support Extended Protection. When we uninstalling the update for KB 980773 from the system the code started to work. But now we had a few questions that we needed answer for:

1)Is there something wrong with Exchange 2007?
2)Does Exchange 2007 not understand the request for Authentication?
3)Why does it still work with port 25 and fails only with port 587?

We then decide to take a verbose log on the Exchange Receive connector and below is what we find:

 >,"220 EX-01.internal.xyz.com Microsoft ESMTP MAIL Service ready at Mon, 15 May 2011 08:59:37 -0500",
<,EHLO akashb,
>,250-EX-01.internal.xyz.com Hello [192.168.111.111],
>,250-SIZE 10485760,
>,250-PIPELINING,
>,250-DSN,
>,250-ENHANCEDSTATUSCODES,
>,250-STARTTLS,
>,250-AUTH GSSAPI NTLM LOGIN,
>,250-8BITMIME,
>,250-BINARYMIME,
>,250 CHUNKING,
<,AUTH gssapi,
>,334 <authentication response>,
>,334 <authentication response>,
*,,Inbound Negotiate failed because of IllegalMessage
>,535 5.7.3 Authentication unsuccessful,
-,,Remote

Looking at the verbose log it looks like Exchange does not understand the message, but why?

Firstly the OS needs to be configured to support Extended Protection and secondly, support for Extended protection was introduced in Exchange 2007 SP 3. The best would be to have Exchange 2007 SP 3 RU 3 v2 installed. In this case it turns out that neither the customer nor I had Exchange 2007 SP 3 RU 3 v2. Make sure you have your SPN’s right. The below article talks about Extended Protection.

Extended Protection for Authentication
https://support.microsoft.com/kb/968389

After Exchange 2007 SP 3 RU 3 v2 is installed you get a new property “ExtendedProtectionPolicy” for the Receive connector. You can then use the Set-ReceiveConnector  cmd-let and set the value for ExtendedProtectionPolicy to “Allow”. This also requires that RequireTLS is set to true on the connector and you must also set EnableSsl = true in the .Net code. So this answers the questions 1 & 2.

Now for the question 3, why does it still work with port 25 and fails only with port 587?

That was easy, the answer lies in EHLO. The Default receive connector (Port 25) did not have GSSAPI enabled and hence it was defaulting to NTLM(which works) instead using Negotiate. In the failing case, Negotiate was used with Extended protection, Exchange did not return the expected data and hence the error.

-Enjoy