• 欢迎访问web前端中文站,JavaScript,CSS3,HTML5,web前端demo
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏web前端中文站吧

记录一次.Net框架Bug发现和提交过程:.Net Framework和.Net Core均受影响

JavaScript web前端中文站 10个月前 (11-14) 417次浏览 已收录 0个评论

SmtpClient 一处代码编写错误导致异步发送邮件时 DeliveryFormat 配置项无法正确工作,异步操作已经完全不受我们设置属性控制了,UTF-8 内容(如中文)转不转码完全看对方邮件服务器心情!

更多精彩内容请看 web 前端中文站
http://www.lisa33xiaoq.net 可按 Ctrl + D 进行收藏

问题已经发到了开发者社区
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 开发者社区富文本编辑器太难用了,还是简书的编辑器好用,然后掘金的版面好看,最后还是喜欢 cnblog 里面可以修改版面 css。

尽瞎说大实话。
重新码一份好看的。

出问题的地方

出现问题的函数:

//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 参数     message.BeginSend(writer, DeliveryMethod != SmtpDeliveryMethod.Network,                             ServerSupportsEai, new AsyncCallback(SendMessageCallback), result.AsyncState);     ... }

ServerSupportsEai所在位置为allowUnicode参数。SmtpClient中所有涉及到allowUnicode参数的地方,赋值都为IsUnicodeSupported()返回值。但唯一这一处是例外。

我们看看IsUnicodeSupported函数:

//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) {     ...     //如果服务器支持 SMTPUTF8,那么 ServerSupportsEai=true     else if (String.Compare(extension, 0, "SMTPUTF8", 0, 8, StringComparison.OrdinalIgnoreCase) == 0) {         ((SmtpPooledStream)pooledStream).serverSupportsEai = true;     }     ... }

产生的现象

SendMailCallbackSmtpClient.SendAsyncSendMailAsync会调用SendAsync)调用的,so,异步操作已经完全不受我们设置的DeliveryFormat参数控制了,UTF-8 内容(如中文)转不转码完全看对方邮件服务器心情!!!

SmtpClient对象DeliveryFormat属性赋值为SmtpDeliveryFormat.SevenBit,要求邮件使用 7 位 ASCII 的传递格式,并且用异步方法来发送;本来Subject附件文件名等里面的 UTF-8 内容(如中文)将会被转码;但如果邮件服务器EHLO返回了SMTPUTF8,那么SmtpClient 对象将会将 UTF-8 内容不转码直接发送出去!导致发送出去的数据内容和预期的数据内容不一致!!!

同步方法Send不受此影响。

解决办法

SendMailCallback函数中的ServerSupportsEai应该换成统一的IsUnicodeSupported,Bug 就解决。

受影响版本

  • .Net Framework 4.5 – 4.7.2(最新版),估计是全系列
  • .Net Core 看最新版也是受此影响

一次.Net 框架 Bug 的发现记录

DKIM 签名功能写好后测试了很多个邮箱,都能通过验证。但隔一天测试却发现没有一个邮箱通过验证,并且下载下来的邮件源码 body 部分和本地额外保存的一份有很大出入,表现在邮件主题、附件文件名,本地是 Base64 编码,下载下来的是中文汉字。

首先发现问题的是 outlook 邮箱,他们家会告诉你 DKIM 签名是否正确,本地直接发送邮件没有一个通过签名验证的,但通过邮箱服务器发送却都是好的。对比直发和服务器发的邮件源码区别,发现邮箱服务器的没有中文,直发的里面中文的地方全是中文。

看样子中文部分有问题,然后试着把邮件里面的中文全部换成英文,发送,又可以了!想了一下昨天测试好像全部是英文,因为邮件内容写了一次基本上就不会改了。

到了这时候,感觉还以为是 outlook 服务器进行了什么处理,难道邮箱服务器发邮件用的协议和我们用 Smtp 协议发邮件的协议有出入?但并没有找到什么相关的资料。然后测试了 QQ 邮箱、网易 yeah.net,并且抓了一下包看了一下,发现切换SmtpClient.DeliveryFormat参数,使用SevenBit(此值为默认值)(中文会被编码)QQ 邮箱没问题,网易有问题;使用International(中文不编码)QQ 邮箱有问题,网易反倒没问题。

抓包发现使用SevenBit时,中文部分给 QQ 邮箱发送的是 Base64 编码,给网易发送的是中文内容,本地保存的是 Base64 编码(和签名时使用到的邮件内容一致);使用International时正好相反。签名的数据和发送的数据不一致,导致了不管怎么改这个参数,都有一个是错的。

为什么会这样?查阅.Net 源码,一路看编码部分,发现基本上每个涉及到字符编码、发送的地方都会传入allowUnicode参数,所有allowUnicode = SmtpClient.IsUnicodeSupported(),但有唯一的一处例外:

[此处忽略,见上文]

SendMailCallbackSmtpClient.SendAsyncSendMailAsync会调用SendAsync)调用的,so,异步操作已经完全不受我们设置的DeliveryFormat参数控制了,中文转不转码完全看对方邮件服务器心情!!!函数中的ServerSupportsEai应该换成统一的IsUnicodeSupported,Bug 就解决。

但,我们没法去改这个地方,那么上 Hook 吧,把SmtpClient.ServerSupportsEaiHook 一下,如果是SendMailCallback调用的就return IsUnicodeSupported()

但,DotNetDetour 库可以 Hook String.Length属性,但没法 HookSmtpClient.ServerSupportsEai属性,不知道啥原因。最后调试烦了放弃了。

结尾使用SmtpClient.Send没有这种问题,就把异步操作全部换成了同步,代码还少了不少。Bug 修理完毕,给 outlook、QQ、网易发英文、中文邮件都能通过 DKIM 签名验证。

【注:本文源自网络文章资源,由站长整理发布】


web 前端中文站 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:记录一次.Net 框架 Bug 发现和提交过程:.Net Framework 和.Net Core 均受影响
喜欢 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址