從.Net框架Bug的提交到修復程式碼成功合併到.NET CoreFX主

funnyok發表於2021-09-09

問題描述

我們用SmtpClientSendAsyncSendMailAsync非同步方法傳送郵件,並且要求使用DeliveryFormat = SmtpDeliveryFormat.SevenBit 格式來編碼中文內容,本來預期是郵件內容中帶中文的SubjectAttachments file name 都會進行Base64編碼。

但實際結果是:如果郵件伺服器支援SMTPUTF8擴充套件,那麼非同步傳送SevenBit郵件並不會進行Base64編碼,同步方法沒有此問題。

問題根源

原因是在SmtpClient.SendMailCallback方法中,message.BeginSend allowUnicode引數直接使用的ServerSupportsEai,而不是統一的IsUnicodeSupported()

 private void SendMailCallback(IAsyncResult result)
 { 
    ......
     _message.BeginSend(_writer, DeliveryMethod != SmtpDeliveryMethod.Network, 
         ServerSupportsEai, new AsyncCallback(SendMessageCallback), result.AsyncState); 
    ......
 }

ServerSupportsEai改成IsUnicodeSupported()問題解決。就這20個字元的改動~

另外附對現有帶Bug的.Net框架修復方法

比如使用的.NET Framework 4.7.2,純天然原生自帶此Bug,我們可以用我們的程式碼修復它。

最開始測試時以為此方法無效,沒想到是Hook錯了地方,換到最深層次呼叫地方,一抓一個準。

使用庫對.Net框架內方法進行Hook,找出SmtpClient.ServerSupportsEai最結果最終是從SmtpConnection.ServerSupportsEai得來的,也許是C#編譯後把整個呼叫過程都最佳化掉了,變成了取值的地方直接呼叫的SmtpConnection中的方法,導致Hook前面的方法都是不會被執行,Hook SmtpConnection.ServerSupportsEai一抓一個準。

附上Hook程式碼:

public class Hook : IMethodMonitor {    public bool ServerSupportsEai {
        [Monitor("System.Net.Mail", "SmtpConnection")]
        get {
            Console.WriteLine("Hook");            return !true?org():false;//什麼情況下要Hook? AsyncLocal和CallContext上下文為什麼在這裡傳不進來?
        }
    }
    [Original]    public bool org() {        return false;
    }
}

另外引出了另外一個折磨人Bug,非同步環境下,ServerSupportsEai的呼叫棧中上下文怎麼會丟失?難道哪裡使用了類似ThreadPool.UnsafeXXX這種效果?我們沒法透過CallContext(AsyncLocal)給Hook程式碼傳引數,只能寫死,不管呼叫方要不要修改返回值,都只能得到修改後的結果,尷尬不尷尬。



作者:高堅果兄弟
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3137/viewspace-2820191/,如需轉載,請註明出處,否則將追究法律責任。

相關文章