CRLF的缺失导致smtp方式发送邮件失败

问题描述

最近在工作中遇到一个问题,客户端(c++)使用smtp方式发送邮件失败,报错如下:

arduino 复制代码
The mail client submitted this message with bare line feeds, which cannot be sent via SMTP protocol DATA command and receiving system does not support BDAT

翻译中中文就是:

复制代码
邮件客户端在提交此邮件时未换行,无法通过 SMTP 协议 DATA 命令发送,且接收系统不支持 BDAT

我们从这个提示中提取出几个关键词:换行、DATA、BDAT。

使用搜索引擎搜索这个错误,看到了一篇微软官方的文章,见参考资料1 。 这片资料中给出的解释和解决方案比较明确: 这里面说,是由于没有将CR-LF追加到消息末尾,并且提示到了RFC2822。

我们下面看一下相关关键词的含义:

  • CR-LF:回车换行,即\r\n。
  • DATA:SMTP协议中的一个命令,用于发送邮件内容。
  • ESMTP: SMTP协议的扩展,即Extended SMTP。
  • BDAT: ESMTP协议中的一个命令,用于发送邮件内容,可以指定数据块的大小。

在RFC2822中,我们可以看到如下内容:

txt 复制代码
Messages are divided into lines of characters.  A line is a series of
   characters that is delimited with the two characters carriage-return
   and line-feed; that is, the carriage return (CR) character (ASCII
   value 13) followed immediately by the line feed (LF) character (ASCII
   value 10).  (The carriage-return/line-feed pair is usually written in
   this document as "CRLF".)

翻译成中文就是:

txt 复制代码
信息被分成一行一行的字符。 一行是一系列
   回车和换行这两个字符来分隔。
   回车(CR)字符(ASCII
   值 13),紧接着是换行(LF)字符(ASCII
   值 10)。 (回车/换行在本文档中通常写为 "CRLF"。
   本文件中通常写为 "CRLF")。

我们再来看一下rfc3030中的内容:

txt 复制代码
A new SMTP verb, BDAT, is defined as an alternative to the "DATA"
      command of [RFC821].  The BDAT verb takes two arguments.  The
      first argument indicates the length, in octets, of the binary data
      chunk.  The second optional argument indicates that the data chunk
      is the last.

      bdat-cmd   ::= "BDAT" SP chunk-size [ SP end-marker ] CR LF
      chunk-size ::= 1*DIGIT
      end-marker ::= "LAST"

大意就是:

txt 复制代码
BDAT,可以作为"DATA"的替代品,BDAT命令有两个参数,第一个参数标识长度,第二次参数标识是否是最后一个数据块。

问题总结

问题原因

客户端在发送的时候,没有将CR-LF追加到消息末尾,导致发送失败。而接收服务器不支持BDAT命令,所以报错。

按照参考资料,准确的说是每一行都应该添加(CR-LF),而不仅是消息末尾。

解决方案

在每一行消息末尾添加(CR-LF)即可。

客户端这样处理后确实正常了。

实际上,很多语言的库都会自动添加(CR-LF),比如go和python。所以大多数时候我们不需要关心这个问题。

参考资料:

本文由mdnice多平台发布

相关推荐
IT_陈寒18 分钟前
SpringBoot启动慢得像蜗牛?原来是这个配置在捣鬼
前端·人工智能·后端
不才不才不不才2 小时前
Spring 源码系列(29): 20 道高频面试题源码级解析合集
java·后端·spring
索隆zoro2 小时前
Army 的可插拔架构:army-jdbc 与方言模块
java·后端
小蒜学长3 小时前
校园社团招新网站的设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端·校园社团招新网站
蜗牛互联网4 小时前
从Barclays扩大Claude部署看受监管软件工程的责任重分配
java·大数据·人工智能·后端·软件工程
周杰伦fans5 小时前
C#对话摘要降低Token消耗
人工智能·后端·c#
做系统的大强6 小时前
脚本说 PASS、OS 读全零 —— 到底是谁在撒谎?
后端·操作系统
code2cat6 小时前
【随笔】MCP资源更新订阅:通知到达以后,Agent怎样刷新旧资料
java·后端·开发工具·ai agent·mcp
Harvil_8 小时前
任务跑一半,API 突然报“对话超长“断流:我给 Agent 修的紧急逃生通道
后端
用户EasyAdminBlazor8 小时前
EasyAdminBlazor SignalR 实时消息源码解析:从 NotificationHub 到站内消息
后端