記錄一次.Net框架Bug發(fā)現(xiàn)和提交過程:SmtpClient一處代碼編寫錯誤導(dǎo)致異步發(fā)送郵件時DeliveryFormat配置項(xiàng)無法正確工作留量,.Net Framework和.Net Core均受影響

問題已經(jīng)發(fā)到了開發(fā)者社區(qū)
https://developercommunity.visualstudio.com/content/problem/381046/smtpclient%E4%B8%80%E5%A4%84%E4%BB%A3%E7%A0%81%E7%BC%96%E5%86%99%E9%94%99%E8%AF%AF%E5%AF%BC%E8%87%B4%E5%BC%82%E6%AD%A5%E5%8F%91%E9%80%81%E9%82%AE%E4%BB%B6%E6%97%B6deliveryformat%E9%85%8D%E7%BD%AE%E9%A1%B9%E6%97%A0%E6%B3%95%E6%AD%A3%E7%A1%AE%E5%B7%A5%E4%BD%9C.html

涉及到的Github倉庫:
https://github.com/xiangyuecn/DKIM-Smtp-csharp

.Net開發(fā)者社區(qū)富文本編輯器太難用了道逗,還是簡書的編輯器好用摊鸡,然后掘金的版面好看孤里,最后還是喜歡cnblog里面可以修改版面css累舷。

盡瞎說大實(shí)話知纷。
重新碼一份好看的壤圃。

出問題的地方

出現(xiàn)問題的函數(shù):

//https://source.dot.net/#System.Net.Mail/System/Net/Mail/SmtpClient.cs,966
//https://referencesource.microsoft.com/#System/net/System/Net/mail/SmtpClient.cs,892

void SendMailCallback(IAsyncResult result) {
    ...
    //注意這個ServerSupportsEai,這個位置是allowUnicode參數(shù)
    message.BeginSend(writer, DeliveryMethod != SmtpDeliveryMethod.Network,
                            ServerSupportsEai, new AsyncCallback(SendMessageCallback), result.AsyncState);
    ...
}

ServerSupportsEai所在位置為allowUnicode參數(shù)琅轧。SmtpClient中所有涉及到allowUnicode參數(shù)的地方伍绳,賦值都為IsUnicodeSupported()返回值。但唯一這一處是例外乍桂。

我們看看IsUnicodeSupported函數(shù):

//https://referencesource.microsoft.com/#System/net/System/Net/mail/SmtpClient.cs,382

private bool IsUnicodeSupported() {
    if (DeliveryMethod == SmtpDeliveryMethod.Network) {
        //注意看這里的ServerSupportsEai和SmtpDeliveryFormat
        return (ServerSupportsEai && (DeliveryFormat == SmtpDeliveryFormat.International));
    }
    else {
        return (DeliveryFormat == SmtpDeliveryFormat.International);
    }
}

DeliveryFormat我們可以賦值冲杀,我們來找找ServerSupportsEai是在哪里取值的:

//https://referencesource.microsoft.com/#System/net/System/Net/mail/smtpconnection.cs,280

internal void ParseExtensions(string[] extensions) {
    ...
    //如果服務(wù)器支持SMTPUTF8,那么ServerSupportsEai=true
    else if (String.Compare(extension, 0, "SMTPUTF8", 0, 8, StringComparison.OrdinalIgnoreCase) == 0) {
        ((SmtpPooledStream)pooledStream).serverSupportsEai = true;
    }
    ...
}

產(chǎn)生的現(xiàn)象

SendMailCallbackSmtpClient.SendAsyncSendMailAsync會調(diào)用SendAsync)調(diào)用的睹酌,so权谁,異步操作已經(jīng)完全不受我們設(shè)置的DeliveryFormat參數(shù)控制了,UTF-8內(nèi)容(如中文)轉(zhuǎn)不轉(zhuǎn)碼完全看對方郵件服務(wù)器心情H碳病4炒!

SmtpClient對象DeliveryFormat屬性賦值為SmtpDeliveryFormat.SevenBit卤妒,要求郵件使用 7 位 ASCII 的傳遞格式甥绿,并且用異步方法來發(fā)送;本來Subject则披、附件文件名等里面的UTF-8內(nèi)容(如中文)將會被轉(zhuǎn)碼共缕;但如果郵件服務(wù)器EHLO返回了SMTPUTF8,那么SmtpClient對象將會將UTF-8內(nèi)容不轉(zhuǎn)碼直接發(fā)送出去士复!導(dǎo)致發(fā)送出去的數(shù)據(jù)內(nèi)容和預(yù)期的數(shù)據(jù)內(nèi)容不一致M脊取!阱洪!

同步方法Send不受此影響便贵。

解決辦法

SendMailCallback函數(shù)中的ServerSupportsEai應(yīng)該換成統(tǒng)一的IsUnicodeSupported,Bug就解決冗荸。

受影響版本

  • .Net Framework 4.5 - 4.7.2(最新版)承璃,估計(jì)是全系列
  • .Net Core 看最新版也是受此影響

一次.Net框架Bug的發(fā)現(xiàn)記錄

DKIM簽名功能寫好后測試了很多個郵箱,都能通過驗(yàn)證蚌本。但隔一天測試卻發(fā)現(xiàn)沒有一個郵箱通過驗(yàn)證盔粹,并且下載下來的郵件源碼body部分和本地額外保存的一份有很大出入隘梨,表現(xiàn)在郵件主題、附件文件名舷嗡,本地是Base64編碼轴猎,下載下來的是中文漢字。

首先發(fā)現(xiàn)問題的是outlook郵箱进萄,他們家會告訴你DKIM簽名是否正確捻脖,本地直接發(fā)送郵件沒有一個通過簽名驗(yàn)證的,但通過郵箱服務(wù)器發(fā)送卻都是好的垮斯。對比直發(fā)和服務(wù)器發(fā)的郵件源碼區(qū)別郎仆,發(fā)現(xiàn)郵箱服務(wù)器的沒有中文,直發(fā)的里面中文的地方全是中文兜蠕。

看樣子中文部分有問題扰肌,然后試著把郵件里面的中文全部換成英文,發(fā)送熊杨,又可以了曙旭!想了一下昨天測試好像全部是英文,因?yàn)猷]件內(nèi)容寫了一次基本上就不會改了晶府。

到了這時候桂躏,感覺還以為是outlook服務(wù)器進(jìn)行了什么處理,難道郵箱服務(wù)器發(fā)郵件用的協(xié)議和我們用Smtp協(xié)議發(fā)郵件的協(xié)議有出入川陆?但并沒有找到什么相關(guān)的資料剂习。然后測試了QQ郵箱、網(wǎng)易yeah.net较沪,并且抓了一下包看了一下鳞绕,發(fā)現(xiàn)切換SmtpClient.DeliveryFormat參數(shù),使用SevenBit(此值為默認(rèn)值)(中文會被編碼)QQ郵箱沒問題尸曼,網(wǎng)易有問題们何;使用International(中文不編碼)QQ郵箱有問題,網(wǎng)易反倒沒問題控轿。

抓包發(fā)現(xiàn)使用SevenBit時冤竹,中文部分給QQ郵箱發(fā)送的是Base64編碼,給網(wǎng)易發(fā)送的是中文內(nèi)容茬射,本地保存的是Base64編碼(和簽名時使用到的郵件內(nèi)容一致)鹦蠕;使用International時正好相反。簽名的數(shù)據(jù)和發(fā)送的數(shù)據(jù)不一致在抛,導(dǎo)致了不管怎么改這個參數(shù)片部,都有一個是錯的。

為什么會這樣霜定?查閱.Net源碼档悠,一路看編碼部分,發(fā)現(xiàn)基本上每個涉及到字符編碼望浩、發(fā)送的地方都會傳入allowUnicode參數(shù)辖所,所有allowUnicode = SmtpClient.IsUnicodeSupported(),但有唯一的一處例外:

[此處忽略磨德,見上文]

SendMailCallbackSmtpClient.SendAsyncSendMailAsync會調(diào)用SendAsync)調(diào)用的缘回,so,異步操作已經(jīng)完全不受我們設(shè)置的DeliveryFormat參數(shù)控制了典挑,中文轉(zhuǎn)不轉(zhuǎn)碼完全看對方郵件服務(wù)器心情K盅纭!您觉!函數(shù)中的ServerSupportsEai應(yīng)該換成統(tǒng)一的IsUnicodeSupported拙寡,Bug就解決。

但琳水,我們沒法去改這個地方肆糕,那么上Hook吧,把SmtpClient.ServerSupportsEaiHook一下在孝,如果是SendMailCallback調(diào)用的就return IsUnicodeSupported()诚啃。

但,DotNetDetour庫可以Hook String.Length屬性私沮,但沒法HookSmtpClient.ServerSupportsEai屬性始赎,不知道啥原因。最后調(diào)試煩了放棄了仔燕。

結(jié)尾使用SmtpClient.Send沒有這種問題造垛,就把異步操作全部換成了同步,代碼還少了不少涨享。Bug修理完畢筋搏,給outlook、QQ厕隧、網(wǎng)易發(fā)英文奔脐、中文郵件都能通過DKIM簽名驗(yàn)證。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吁讨,一起剝皮案震驚了整個濱河市髓迎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌建丧,老刑警劉巖排龄,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異翎朱,居然都是意外死亡橄维,警方通過查閱死者的電腦和手機(jī)尺铣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來争舞,“玉大人凛忿,你說我怎么就攤上這事【捍ǎ” “怎么了店溢?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長委乌。 經(jīng)常有香客問我床牧,道長,這世上最難降的妖魔是什么遭贸? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任戈咳,我火速辦了婚禮,結(jié)果婚禮上革砸,老公的妹妹穿的比我還像新娘除秀。我一直安慰自己,他們只是感情好算利,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布册踩。 她就那樣靜靜地躺著,像睡著了一般效拭。 火紅的嫁衣襯著肌膚如雪暂吉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天缎患,我揣著相機(jī)與錄音慕的,去河邊找鬼。 笑死挤渔,一個胖子當(dāng)著我的面吹牛肮街,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播判导,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼嫉父,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了眼刃?” 一聲冷哼從身側(cè)響起绕辖,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎擂红,沒想到半個月后仪际,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年树碱,在試婚紗的時候發(fā)現(xiàn)自己被綠了肯适。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡赴恨,死狀恐怖疹娶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情伦连,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布钳垮,位于F島的核電站惑淳,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏饺窿。R本人自食惡果不足惜歧焦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望肚医。 院中可真熱鬧绢馍,春花似錦、人聲如沸肠套。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽你稚。三九已至瓷耙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間刁赖,已是汗流浹背搁痛。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留宇弛,地道東北人鸡典。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像枪芒,于是被迫代替她去往敵國和親彻况。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容