从发现. NET Framework 中 SmtpClient 的 Bug 并拿出解决方案, 然后给微软开发者社区提交 Bug 开始, 总共耗时一个多月, 对 Bug 修复的代码最终被采纳, 现已合并到. NET Core Libraries (CoreFX) 主线中.
修复记录
虽然对 Bug 修复实际生效的代码只有 20 个字符, 但意义重大, 这个 Bug 不遇上一点事没有, 遇上了不对框架开刀是非常棘手的, 而且备受折磨也稍微有点难摸得清头脑.
相关详情见我的另外一篇博客《记录一次. Net 框架 Bug 发现和提交过程:.Net Framework 和. Net Core 均受影响》
问题描述
我们用 SmtpClient 的 SendAsync, SendMailAsync 异步方法发送邮件, 并且要求使用 DeliveryFormat = SmtpDeliveryFormat.SevenBit 格式来编码中文内容, 本来预期是邮件内容中带中文的 Subject,Attachments 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 错了地方, 换到最深层次调用地方, 一抓一个准.
使用 https://github.com/bigbaldy1128/DotNetDetour 库对. 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 代码传参数, 只能写死, 不管调用方要不要修改返回值, 都只能得到修改后的结果, 尴尬不尴尬.
来源: https://www.cnblogs.com/xiangyuecn/p/10122764.html