黃偉榮的學習筆記

軟體的世界變化萬千,小小的我只能在這洪流奮發向上以求立足。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
今天在寫SmtpClient寄Mail,用自己架的MailServer正常,但用對方的MailServer都是失敗,發現是在驗證方面出現,但Outlook寄又是正常,找不出結果下使用封包監聽軟體(Wireshark)去看這SmtpClient與Outlook寄出的有什麼不同,最後使用Reflector找出怎麼設定驗證方式。

這是測試的專案
SmtpTest.rar



這是捕捉Outlook封包內容(塗黑的是隱私的資訊如Host、UserID等)
紅色:本機送出的文字
藍色:MailServer回傳的文字


這是補捉SmtpClient的封包內容



從中發現Auth outlock是用Login(只用Base64加密),而SmtpClient是用GSSAPI(上網查是Kerberos的一種,Win2000(沒升級SP2)還不支源這種)。
由此可知,會錯的原因是客戶的MailServer,用GSSAPI驗證會失敗,可以要怎麼改StmpClient方式呢?

今天以前我只知道用這樣的方式 : smtpClient.Credentials = new NetworkCredential(userNameTextBox.Text, passwordTextBox.Text);

Google查也沒有找到更改験證的方法,只好用Reflector看看他是怎麼實作的。

注:下列都省略System.Net.Mail的命名空間

查到是在SmtpConnection.GetConnection方法中的(Reflector沒有行號)
....
NetworkCredential credential = this.credentials.GetCredential(host, port, this.authenticationModules[i].AuthenticationType);
if (credential != null)
{
 ....
}
....
這幾行去決定驗證方法
而authenticationModules屬性是ISmtpAuthenticationModule陣列
成員有這四個
SmtpNegotiateAuthenticationModule AuthenticationType=gssapi
SmtpNtlmAuthenticationModule AuthenticationType=ntlm
SmtpNtlmAuthenticationModule AuthenticationType=WDigest
SmtpNtlmAuthenticationModule AuthenticationType=login

會跑四次用SmtpClient.Credentials.GetCredential方法檢查是否支援本Module。
 
而SmtpClient.Credentials是ICredentialsByHost介面
實作ICredentialsByHost的有這二個
CredentialCache
NetworkCredential

NetworkCredential
public NetworkCredential GetCredential(string host, int port, string authenticationType)
{
    return this;
}
不管什麼Module都支援(所以使用第一個Module SmtpNegotiateAuthenticationModule)。

CredentialCache
public NetworkCredential GetCredential(string host, int port, string authenticationType)
{    
    NetworkCredential credential = null;
    IDictionaryEnumerator enumerator = this.cacheForHosts.GetEnumerator();
    while (enumerator.MoveNext())
    {
    CredentialHostKey key = (CredentialHostKey) enumerator.Key;
    if (key.Match(host, port, authenticationType))
    {
        credential = (NetworkCredential) enumerator.Value;
    }
    }
    return credential;
}
會比較AuthenticationType

結語

所以StmpClient要改變驗證方式的話要用CredentialCache類別

CredentialCache myCache = new CredentialCache();
myCache.Add(hostTextBox.Text, 25, "login", new NetworkCredential(userNameTextBox.Text, passwordTextBox.Text));
smtpClient.Credentials = myCache;

第三個參數不能打錯(打錯就Match不到了,視同沒有驗證),有這下列四種(從Module看出),不分大小寫。
gssapi
ntlm
WDigest
login

這樣就跟用Outlook寄出的封包內容一模一樣了。