<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[向东博客 专注WEB应用 构架之美 --- 构架之美，在于尽态极妍 | 应用之美，在于药到病除]]></title> 
<link>http://jackxiang.com/index.php</link> 
<description><![CDATA[赢在IT，Playin' with IT,Focus on Killer Application,Marketing Meets Technology.]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[向东博客 专注WEB应用 构架之美 --- 构架之美，在于尽态极妍 | 应用之美，在于药到病除]]></copyright>
<item>
<link>http://jackxiang.com/post//</link>
<title><![CDATA[利用SMTP发送Mail详解]]></title> 
<author>jack &lt;xdy108@126.com&gt;</author>
<category><![CDATA[WEB2.0]]></category>
<pubDate>Sun, 21 Mar 2010 15:38:16 +0000</pubDate> 
<guid>http://jackxiang.com/post//</guid> 
<description>
<![CDATA[ 
	这个哥们写的邮件的分析很不错，转载自：http://blog.csdn.net/study_live/archive/2010/01/09/5163639.aspx<br/><br/><br/><br/>在以前接触的项目中，一直都是在做网站时用到了发送mail 的功能，在asp 和.net 中都有相关的发送mail 的类, 实现起来非常简单。最近这段时间因工作需要在C++ 中使用发送mail 的功能，上网搜了一大堆资料，终于得以实现，总结自己开发过程中碰到的一些问题，希望对需的人有所帮助, 由于能力有限, 文中不免有些误解之处, 望大家能指正!!<br/><br/>其实，使用C++ 发送mail 也是很简的事, 只需要了解一点SMTP 协议和socket 编程就OK 了, 网络上也有很多高人写好的mail 类源码，有兴趣的朋友可以下载看看.<br/><br/> <br/><br/>1.&nbsp;&nbsp;&nbsp;&nbsp; SMTP 常用命令简介<br/><br/>1). SMTP 常用命令<br/><br/>HELO/EHLO 向服务器标识用户身份<br/><br/>MAIL 初始化邮件传输<br/><br/>mail from:<br/><br/>RCPT 标识单个的邮件接收人；常在MAIL 命令后面<br/><br/>可有多个rcpt to:<br/><br/>DATA 在单个或多个RCPT 命令后，表示所有的邮件接收人已标识，并初始化数据传输，以. 结束。<br/><br/>VRFY 用于验证指定的用户/ 邮箱是否存在；由于安全方面的原因，服务器常禁止此命令<br/><br/>EXPN 验证给定的邮箱列表是否存在，扩充邮箱列表，也常被禁用<br/><br/>HELP 查询服务器支持什么命令<br/><br/>NOOP 无操作，服务器应响应OK<br/><br/>QUIT 结束会话<br/><br/>RSET 重置会话，当前传输被取消<br/><br/> <br/><br/>如你对SMTP 命令不了解，可以用telnet 命令登陆到smtp 服务器用help 命令进行查看:<br/><br/>Normal 0 7.8 磅 0 2 false false false MicrosoftInternetExplorer4<br/><br/>220 tdcsw.maintek.corpnet.asus ESMTP Sendmail 8.13.8/8.13.8; Sat, 9 Jan 2010 10:<br/>45:09 +0800<br/>help<br/>214-2.0.0 This is sendmail<br/>214-2.0.0 Topics:<br/>214-2.0.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HELO&nbsp;&nbsp;&nbsp;&nbsp;EHLO&nbsp;&nbsp;&nbsp;&nbsp;MAIL&nbsp;&nbsp;&nbsp;&nbsp;RCPT&nbsp;&nbsp;&nbsp;&nbsp;DATA<br/>214-2.0.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RSET&nbsp;&nbsp;&nbsp;&nbsp;NOOP&nbsp;&nbsp;&nbsp;&nbsp;QUIT&nbsp;&nbsp;&nbsp;&nbsp;HELP&nbsp;&nbsp;&nbsp;&nbsp;VRFY<br/>214-2.0.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EXPN&nbsp;&nbsp;&nbsp;&nbsp;VERB&nbsp;&nbsp;&nbsp;&nbsp;ETRN&nbsp;&nbsp;&nbsp;&nbsp;DSN&nbsp;&nbsp;&nbsp;&nbsp; AUTH<br/>214-2.0.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; STARTTLS<br/>214-2.0.0 For more info use &quot;HELP &lt;topic&gt;&quot;.<br/>214-2.0.0 To report bugs in the implementation see<br/>214-2.0.0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; http://www.sendmail.org/email-addresses.html<br/>214-2.0.0 For local information send email to Postmaster at your site.<br/>214 2.0.0 End of HELP info<br/><br/><br/>2).SMTP 返回码含义<br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 邮件服务返回代码含义 <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 500&nbsp;&nbsp; 格式错误，命令不可识别（此错误也包括命令行过长） <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 501&nbsp;&nbsp; 参数格式错误 <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 502&nbsp;&nbsp; 命令不可实现 <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 503&nbsp;&nbsp; 错误的命令序列 <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 504&nbsp;&nbsp; 命令参数不可实现 <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 211&nbsp;&nbsp;&nbsp;&nbsp;系统状态或系统帮助响应 <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 214&nbsp;&nbsp; 帮助信息 <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 220&nbsp;&nbsp;&nbsp;&nbsp; 服务就绪 <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 221&nbsp;&nbsp;&nbsp;&nbsp; 服务关闭传输信道 <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 421&nbsp;&nbsp;&nbsp;&nbsp; 服务未就绪，关闭传输信道（当必须关闭时，此应答可以作为对任何命令的响应） <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 250&nbsp;&nbsp; 要求的邮件操作完成 <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 251&nbsp;&nbsp; 用户非本地，将转发向 <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 450&nbsp;&nbsp; 要求的邮件操作未完成，邮箱不可用（例如，邮箱忙） <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 550&nbsp;&nbsp; 要求的邮件操作未完成，邮箱不可用（例如，邮箱未找到，或不可访问） <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 451&nbsp;&nbsp; 放弃要求的操作；处理过程中出错 <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 551&nbsp;&nbsp; 用户非本地，请尝试 <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 452&nbsp;&nbsp; 系统存储不足，要求的操作未执行 <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 552&nbsp;&nbsp; 过量的存储分配，要求的操作未执行 <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 553&nbsp;&nbsp; 邮箱名不可用，要求的操作未执行（例如邮箱格式错误） <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 354&nbsp;&nbsp; 开始邮件输入，以. 结束 <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 554&nbsp;&nbsp; 操作失败 <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 535&nbsp;&nbsp; 用户验证失败 <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 235&nbsp;&nbsp; 用户验证成功 <br/><br/>&nbsp;&nbsp;*&nbsp;&nbsp; 334&nbsp;&nbsp; 等待用户输入验证信息 for next connection&gt;;<br/><br/> <br/><br/>3) SMTP 命令应用<br/><br/>我们下需使用telnet 命令实现smtp 邮件的发送，具体操作如下:<br/><br/>220 tdcsw.com ESMTP Sendmail 8.13.8/8.13.8; Wed, 23 Dec 2009 18<br/>:18:18 +0800<br/>HELO tdcsw<br/>250 tdcsw.com Hello x-128-101-1-240.ahc.umn.edu [128.101.1.240], pleased to meet you<br/>MAIL FROM:lily@tdcsw.com<br/>250 2.1.0 lily@tdcsw.com... Sender ok<br/>RCPR TO:sam@163.com<br/>250 2.1.5 carven@tdcsw.pegatroncorp.com... Recipient ok<br/>DATA<br/>354 Enter mail, end with &quot;.&quot; on a line by itself<br/>SUBJECT:HELLO<br/>HI:<br/>HAR are you?<br/>.<br/>250 2.0.0 nBNAIIG4000507 Message accepted for delivery<br/>quit<br/>221 2.0.0 tdcsw.maintek.corpnet.asus closing connection<br/>Connection to host lost.<br/><br/> <br/><br/>2.&nbsp;&nbsp;&nbsp;&nbsp; 用C++ 实现Mail 发送<br/><br/>为了便于理解, 在此就不封装Mail 类了, 而是以过程式函数方式给出.<br/><br/>1). 首先需要建立TCP 套接字, 连接端口依服务器而定，SMTP 服务默认端口为25, 我们以 默认端口为例<br/><br/>WSADATA wsaData;<br/><br/>int&nbsp;&nbsp;SockFD;<br/><br/>WSAStartup(MAKEWORD(2,2), &amp;wsaData);<br/><br/>SockFD = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);<br/><br/>ServAddr.sin_family = AF_INET;<br/><br/>ServAddr.sin_addr.s_addr = inet_addr (“192.168.1.1”);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //192.168.1.1 为服务器地址<br/><br/>ServAddr.sin_port = htons(25);<br/><br/>connect(SockFD, (struct sockaddr *)&amp;ServAddr, sizeof(ServAddr));<br/><br/>2). 发送SMTP 命令及数据<br/><br/>const char HEADER[] = &quot;HELO smtpSrv&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;MAIL FROM: sender@126.com&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;RCPT TO: recv@gmail.com&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;DATA&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;FROM: sender@126.com&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;TO: recv@gmail.com&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;SUBJECT: this is a test&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;Date: Fri, 8 Jan 2010 16:12:30&#92;r&#92;n&quot;<br/><br/>&quot;X-Mailer: shadowstar&#039;s mailer&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;MIME-Version: 1.0&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;Content-type: text/plain&#92;r&#92;n&#92;r&#92;n&quot;;<br/><br/>//send HEADER<br/><br/>send(SockFD, HEADER, strlen(HEADER), 0);<br/><br/> <br/><br/>const char CONTENT[]=&quot;this is content.&#92;r&#92;n&quot;;<br/><br/>//send CONTENT<br/><br/>send(SockFD, CONTENT, strlen(CONTENT), 0);<br/><br/> <br/><br/>send(SockFD, &quot;.&#92;r&#92;n&quot;, strlen(&quot;.&#92;r&#92;n&quot;), 0);&nbsp;&nbsp; //end<br/><br/>send(SockFD, &quot;QUIT&#92;r&#92;n&quot;, strlen(&quot;QUIT&#92;r&#92;n&quot;), 0); //quit<br/><br/> <br/><br/>mail 发送的功能基本上就完成了, 当然, 如果是应用的话还是需要很多改动的地方的, 比如说添加附件等.<br/><br/>3). 附件功能<br/><br/>要使用SMTP 发送附件, 需要对SMTP 头信息进行说明, 改变Content-type 及为每一段正文添加BOUNDARY 名, 示例如下:<br/><br/>&quot;DATA&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;FROM: sender@126.com&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;TO: recv@gmail.com&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;SUBJECT: this is a test&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;Date: Fri, 8 Jan 2010 16:12:30&#92;r&#92;n&quot;<br/><br/>&quot;X-Mailer: shadowstar&#039;s mailer&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;MIME-Version: 1.0&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;Content-type: multipart/mixed; boundary=&#92;&quot;#BOUNDARY#&#92;&quot;&#92;r&#92;n&#92;r&#92;n&quot;;<br/><br/> <br/><br/>// 正文<br/><br/>&quot;--#BOUNDARY#&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;Content-Type: text/plain; charset=gb2312&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;Content-Transfer-Encoding: quoted-printable&#92;r&#92;n&quot;<br/><br/>邮件正文……….<br/><br/> <br/><br/>// 附件<br/><br/>&quot;&#92;r&#92;n--#BOUNDARY#&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;Content-Type: application/octet-stream; name=att.txt&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;Content-Disposition: attachment; filename=att.txt&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;Content-Transfer-Encoding: base64&#92;r&#92;n&quot;<br/><br/>&nbsp;&nbsp;&quot;&#92;r&#92;n&quot;<br/><br/>附件正信息(base64 编码)…..<br/><br/> <br/><br/>Base64 编码函数在网络上很容易找到, 这里就不给出源码了, 如需要支持HTML 格式而又不知道如何写这些头信息, 可以用outlook 或foxmail 写一封支持HTML 格式的mail, 查看其原文信息, 依照相同的格式发送就行了.<br/><br/> <br/><br/>4). 实现抄送及密送<br/><br/>在SMTP 命令集中并没有RCPT CC 或RCPT BCC 相关命令, 那要如何来实现抄送和密送功能呢?<br/><br/>在网络上找到这样一句话: “ 所有的接收者协商都通过RCPT TO 命令来实现，如果是BCC ，则协商发送后在对方接收时被删掉信封接收者”, 开始一直不明白这句话是什么意思? 后来通看查看foxmail 的邮件原文发现:<br/><br/>Date: Wed, 6 Jan 2010 12:11:48 +0800<br/><br/>From: &quot;carven_li&quot; &lt; carven_li @smtp.com&gt;<br/><br/>To: &quot;carven&quot; &lt;carven@smtp.com&gt;<br/><br/>Cc: &quot;sam&quot; &lt;sam@smtp.com&gt;,<br/><br/>&nbsp;&nbsp;&quot;yoyo&quot; &lt;yoyo@smtp.com&gt;<br/><br/>BCC: &quot;clara&quot; &lt;clara@tsmtp.com&gt;<br/><br/>Subject: t<br/><br/>X-mailer: Foxmail 5.0 [cn]<br/><br/>Mime-Version: 1.0<br/><br/>Content-Type: multipart/mixed;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;boundary=&quot;=====001_Dragon237244850520_=====&quot;<br/><br/>才恍然大悟, 所谓的” 协商” 应该就是指发送方在Data 中指定哪些为CC, 哪些为BCC, 默认情况下什么都不写, 只发送第一个RCPT TO 的mail, 其他的都被过滤掉. <br/><br/>3. SMTP身份认证<br/>SMTP身份认证方式有很多种, 每种认证方式验证发送的信息都有点细微的差别, 这里我主要介绍下LOGIN,PLAIN及NTLM三种简单的认证方式, 附带CRAM-MD5和DIGEST-MD5方式(验证没通过, 不知道问题出在哪了? 有待高人帮忙解决!).<br/><br/>要进行身份认证, 先要知道当前SMTP服务器支持哪些认证方式, 在ESMTP中有个与HELO命令相同功能的命令EHLO可以得到当前服务器支持的认证方式(有些服务器无返回信息, 可能服务器端作了限制).<br/> <br/><br/>1) LOGIN认证方式<br/>LOGIN认证方式是基于明文传输的, 因此没什么安全性可言, 如信息被截获, 那么用户名和密码也就泄露了. 认证过程如下:<br/>AUTH LOGIN<br/>334 VXNlcm5hbWU6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//服务器返回信息, Base64编码的Username:<br/>bXlOYW1l&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//输入用户名, 也需Base64编码<br/>334 UGFzc3dvcmQ6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//服务器返回信息, Base64编码的Password::<br/>bXlQYXNzd29yZA==&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//输入密码, 也需Base64编码<br/>235 2.0.0 OK Authenticated&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 535 5.7.0 authentication failed<br/><br/>2). NTLM认证方式<br/>NTLM认证方式过程与LOGIN认证方式是一模一样的, 只需将AUTH LOGN改成AUTH NTLM.就行了.<br/><br/>3). PLAIN认证方式<br/>PLAIN认证方式消息过过程与LOGIN和NTLM有所不同, 其格式为: “NULL+UserName+NULL+Password”, 其中NULL为C语言中的’&#92;0’, 不方便使用命令行测试, 因此下面给出C++代码来实现:<br/>char szSend[] = &quot;$user$pwd&quot;;<br/>size_t n = stlen(szSend);<br/>for(int i=0; i&lt;n; i++)<br/>&nbsp;&nbsp;&nbsp;&nbsp;if(szSend[i] == &#039;$&#039;) szSend[i] = &#039;&#92;0&#039;;<br/><br/>char szMsg[512]<br/>base64_encode(szSend, n, szMsg);<br/>send (skt, szMsg, strlen(szMsg), 0);<br/><br/>4). CRAM-MD5认证方式<br/>前面所介绍的三种方式, 都是将用户名和密码经过BASE64编码后直接发送到服务器端的, BASE64编码并不是一种安全的加密算法, 其所有信息都可能通过反编码, 没有什么安全性可言. 而CRAM-MD5方式与前三种不同, 它是基于Challenge/Response的方式, 其中Challenge是由服务器产生的, 每次连接产生的Challenge都不同, 而Response是由用户名,密码,Challenge组合而成的, 具体格式如下:<br/>response=base64_encode(username : H_MAC(challenge, password))<br/>H_MAC是Keyed MD5算法(见http://www.faqs.org/rfcs/rfc2195.html), 先由challenge和password生成16位的散列码, 将其转换成16进制32个字节的字符串数组digest(即以%02x输出), 再对(username+空格+digest[32])进行base64编码,就是要发送的response了.<br/>另外, 在http://www.net-track.ch/opensource/cmd5/提供了SMTP CRAM-MD5认证源码, 可用于测试CRAM-MD5认证, 但不知道是不是我这边测试的SendMail服务器配置有问题, 测试时一直不能通过.<br/>5). DIGEST-MD5认证方式<br/>DIGEST-MD5认证也是Challenge/Response的方式, 与CRAM-MD5相比, 它的Challenge信息更多, 其Response计算方式也非常复杂, 我在测试时也是以认证失败而告终, 只是将在网上找到的资料整理于此, 能为后来研究的人多提供点资料, 或者有兴趣的朋友们可以和我一起讨论下.<br/><br/>我们先看下DIGEST-MD5认证发送响应信息:<br/><br/>DIGEST-MD5服务器格式说明(见RFC 2831 Digest SASL Mechanism Mai 2000):<br/>&nbsp;&nbsp; digest-challenge =<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1 # (Reich &#124; Nonce &#124; qop-Optionen &#124; schal &#124; MAXBUF &#124; charset<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Algorithmus &#124; Chiffre-opts &#124; auth-param)<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;realm = &quot;Reich&quot; &quot;=&quot; &lt; &quot;&gt; Reich-Wert &lt;&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Reich-Wert = qdstr-val<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Nonce = &quot;Nonce&quot; &quot;=&quot; &lt; &quot;&gt; Nonce-Wert &lt;&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Nonce-Wert = qdstr-val<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;qop-options = &quot;qop&quot; &quot;=&quot; &lt; &quot;&gt; qop-Liste &lt;&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;qop-list = 1 # qop-Wert<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;qop-Wert = &quot;auth&quot; &#124; &quot;auth-int&quot; &#124; &quot;auth-conf&quot; &#124;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Token<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stale = &quot;veraltete&quot; &quot;=&quot; &quot;true&quot;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MAXBUF = &quot;MAXBUF&quot; &quot;=&quot; MAXBUF-Wert<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MAXBUF-Wert = 1 * DIGIT<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;charset = &quot;charset&quot; = &quot;&quot; UTF-8 &quot;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;algorithm = &quot;Algorithmus&quot; &quot;=&quot; &quot;md5-sess&quot;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Chiffre-opts = &quot;Chiffre&quot; &quot;=&quot; &lt; &quot;&gt; 1 # Null-Wert &lt;&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Chiffre-value = &quot;3des&quot; &#124; &quot;des&quot; &#124; &quot;RC4-40&quot; &#124; &quot;RC4&quot; &#124;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;RC4-56&quot; &#124; Token<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;auth-param = Token &quot;=&quot; (token &#124; quoted-string)<br/>DIGEST-MD5客户端响应格式说明(见RFC 2831 Digest SASL Mechanism Mai 2000):<br/>&nbsp;&nbsp; digest-response = 1 # (Benutzername &#124; Reich &#124; Nonce &#124; cnonce &#124;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Nonce-count &#124; qop &#124; digest-uri &#124; Antwort &#124;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MAXBUF &#124; charset &#124; Chiffre &#124; authzid &#124;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;auth-param)<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; username = &quot;username&quot; = &quot;&lt;&quot;&gt; username-Wert &lt; &quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Benutzernamen-Wert = qdstr-val<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cnonce = &quot;cnonce&quot; &quot;=&quot; &lt; &quot;&gt; cnonce-Wert &lt;&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cnonce-Wert = qdstr-val<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Nonce-count = &quot;nc&quot; &quot;=&quot; nc-Wert<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nc-Wert = 8LHEX<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; qop = &quot;qop&quot; &quot;=&quot; qop-Wert<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; digest-uri = &quot;digest-uri&quot; = &quot;&lt;&quot;&gt; digest-uri-value &lt; &quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; digest-uri-value = serv-type &quot;/&quot; host [ &quot;/&quot; serv-name]&nbsp;&nbsp; //eg: smtp/mail3.example.com/example.com<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; serv-type = 1 * ALPHA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//www for web-service, ftp for ftp-dienst, SMTP for mail-versand-service …<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; host = 1 * (ALPHA &#124; DIGIT &#124; &quot;-&quot; &#124; &quot;.&quot;)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; serv-name = host<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; response = &quot;Antwort&quot; &quot;=&quot; Response-Wert<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; response-value = 32LHEX<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LHEX = &quot;0&quot; &#124; &quot;1&quot; &#124; &quot;2&quot; &#124; &quot;3&quot; &#124;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;4&quot; &#124; &quot;5&quot; &#124; &quot;6&quot; &#124; &quot;7&quot; &#124;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;8&quot; &#124; &quot;9&quot; &#124; &quot;a&quot; &#124; &quot;b&quot; &#124;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;c&quot; &#124; &quot;d&quot; &#124; &quot;e&quot; &#124; &quot;f&quot;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cipher = &quot;Chiffre&quot; &quot;=&quot; Null-Wert<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; authzid = &quot;authzid&quot; &quot;=&quot; &lt; &quot;&gt; authzid-Wert &lt;&quot;&gt;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; authzid-Wert = qdstr-val<br/><br/>其各字段具体含义见相关文档, 这里只介始几个需要用到的字段是如何产生的, C/S响应示例如下:<br/>&nbsp;&nbsp;&nbsp;&nbsp;S: realm=&quot;elwood.innosoft.com&quot;,nonce=&quot;OA6MG9tEQGm2hh&quot;,qop=&quot;auth&quot;,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; algorithm=md5-sess,charset=utf-8<br/>&nbsp;&nbsp;&nbsp;&nbsp;C: charset=utf-8,username=&quot;chris&quot;,realm=&quot;elwood.innosoft.com&quot;,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nonce=&quot;OA6MG9tEQGm2hh&quot;,nc=00000001,cnonce=&quot;OA6MHXh6VqTrRk&quot;,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; digest-uri=&quot;imap/elwood.innosoft.com&quot;,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; response=d388dad90d4bbd760a152321f2143af7,qop=auth<br/>&nbsp;&nbsp;&nbsp;&nbsp;S: rspauth=ea40f60335c427b5527b84dbabcdfffd<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;The password in this example was &quot;secret&quot;.<br/>从这个示例可以看出, 客户端返回的信息比服务器端发送过来的多了以下几个:<br/>username, nc, cnonce, digest-uri和respone<br/>username就不用说了, nc是8位长的16进制数字符串,统计客户端使用nonce发出请求的次数(包含当前请求),例示我们可以设为”00000001”, cnonce是是用了4个随机数组成一个8位长16进制的字符串,digest-uri是由在realm前加上请求类型(如http, smtp等), response是一个32位长的16进制数组, 计算公式如下:<br/>If the &quot;qop&quot; value is &quot;auth&quot; or &quot;auth-int&quot;:<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;request-digest&nbsp;&nbsp;= &lt;&quot;&gt; &lt; KD ( H(A1),&nbsp;&nbsp;&nbsp;&nbsp; unq(nonce-value)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;:&quot; nc-value<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;:&quot; unq(cnonce-value)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;:&quot; unq(qop-value)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;:&quot; H(A2)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;) &lt;&quot;&gt;<br/>&nbsp;&nbsp; If the &quot;qop&quot; directive is not present (this construction is for<br/>&nbsp;&nbsp; compatibility with RFC 2069):<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;request-digest&nbsp;&nbsp;=<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;&quot;&gt; &lt; KD ( H(A1), unq(nonce-value) &quot;:&quot; H(A2) ) &gt;<br/>&nbsp;&nbsp; &lt;&quot;&gt;<br/>&nbsp;&nbsp; See below for the definitions for A1 and A2.<br/>Read more: http://www.faqs.org/rfcs/rfc2617.html#ixzz0c4s8ck3F<br/><br/>KD（secret,data）表示分类算法，其中data指数据，secret表示采用的方法.如果表示校验和算法时，data要写成H（data）；而unq（X）表示将带引号字符串的引号去掉。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 对于&quot;MD5&quot; 和&quot;MD5-sess&quot; 算法：<br/>H(data) = MD5(data)<br/>和<br/>KD(secret, data) = H(concat(secret, &quot;:&quot;, data))<br/><br/>如果&quot;algorithm&quot;指定为&quot;MD5&quot;或没有指定,A1计算方式如下:<br/>A1&nbsp;&nbsp;=&nbsp;&nbsp;unq(username-value) &quot;:&quot; unq(realm-value) &quot;:&quot; passwd<br/>//Password为用户密码<br/>如果&quot;algorithm&quot;指定为&quot;MD5-sess&quot;, 则需要nonce和cnonce的参与:<br/>A1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = H(unq(username-value) &quot;:&quot; unq(realm-value) &quot;:&quot; passwd )<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;:&quot; unq(nonce-value) &quot;:&quot; unq(cnonce-value)<br/><br/>如果&quot;qop&quot;没有指定或指定为&quot;auth&quot;, A2计算方式如下:<br/>A2&nbsp;&nbsp;= Method &quot;:&quot; digest-uri-value<br/>如果&quot;qop&quot;没有指定或指定为&quot;auth int&quot;, A2计算方式如下:<br/>A2 = Method &quot;:&quot; digest-uri-value &quot;:&quot; H(entity-body)<br/><br/>Method是http请求时的方法(post,get), 由于英文水平比较差, 很多都看不明白, 有兴趣的朋友可以自己去看看原文(http://www.faqs.org/rfcs/rfc2617.html), 这里还提供了DIGEST验证的代码:<br/>&nbsp;&nbsp; File &quot;digcalc.h&quot;:<br/><br/>#define HASHLEN 16<br/>typedef char HASH[HASHLEN];<br/>#define HASHHEXLEN 32<br/>typedef char HASHHEX[HASHHEXLEN+1];<br/>#define IN<br/>#define OUT<br/><br/>/* calculate H(A1) as per HTTP Digest spec */<br/>void DigestCalcHA1(<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszAlg,<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszUserName,<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszRealm,<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszPassword,<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszNonce,<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszCNonce,<br/>&nbsp;&nbsp;&nbsp;&nbsp;OUT HASHHEX SessionKey<br/>&nbsp;&nbsp;&nbsp;&nbsp;);<br/><br/>/* calculate request-digest/response-digest as per HTTP Digest spec */<br/>void DigestCalcResponse(<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN HASHHEX HA1,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* H(A1) */<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszNonce,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* nonce from server */<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszNonceCount,&nbsp;&nbsp;/* 8 hex digits */<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszCNonce,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/* client nonce */<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszQop,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* qop-value: &quot;&quot;, &quot;auth&quot;, &quot;auth-int&quot; */<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszMethod,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/* method from the request */<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszDigestUri,&nbsp;&nbsp; /* requested URL */<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN HASHHEX HEntity,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* H(entity body) if qop=&quot;auth-int&quot; */<br/>&nbsp;&nbsp;&nbsp;&nbsp;OUT HASHHEX Response&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/* request-digest or response-digest */<br/>&nbsp;&nbsp;&nbsp;&nbsp;);<br/><br/>File &quot;digcalc.c&quot;:<br/><br/>#include &lt;global.h&gt;<br/>#include &lt;md5.h&gt;<br/><br/>#include &lt;string.h&gt;<br/>#include &quot;digcalc.h&quot;<br/><br/>void CvtHex(<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN HASH Bin,<br/>&nbsp;&nbsp;&nbsp;&nbsp;OUT HASHHEX Hex<br/>&nbsp;&nbsp;&nbsp;&nbsp;)<br/>{<br/>&nbsp;&nbsp;&nbsp;&nbsp;unsigned short i;<br/>&nbsp;&nbsp;&nbsp;&nbsp;unsigned char j;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;for (i = 0; i &lt; HASHLEN; i++) {<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;j = (Bin[i] &gt;&gt; 4) &amp; 0xf;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (j &lt;= 9)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Hex[i*2] = (j + &#039;0&#039;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Hex[i*2] = (j + &#039;a&#039; - 10);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;j = Bin[i] &amp; 0xf;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (j &lt;= 9)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Hex[i*2+1] = (j + &#039;0&#039;);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Hex[i*2+1] = (j + &#039;a&#039; - 10);<br/>&nbsp;&nbsp;&nbsp;&nbsp;};<br/>&nbsp;&nbsp;&nbsp;&nbsp;Hex[HASHHEXLEN] = &#039;&#92;0&#039;;<br/>};<br/><br/>/* calculate H(A1) as per spec */<br/>void DigestCalcHA1(<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszAlg,<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszUserName,<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszRealm,<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszPassword,<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszNonce,<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszCNonce,<br/>&nbsp;&nbsp;&nbsp;&nbsp;OUT HASHHEX SessionKey<br/>&nbsp;&nbsp;&nbsp;&nbsp;)<br/>{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5_CTX Md5Ctx;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HASH HA1;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Init(&amp;Md5Ctx);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, pszUserName, strlen(pszUserName));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, &quot;:&quot;, 1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, pszRealm, strlen(pszRealm));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, &quot;:&quot;, 1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, pszPassword, strlen(pszPassword));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Final(HA1, &amp;Md5Ctx);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (stricmp(pszAlg, &quot;md5-sess&quot;) == 0) {<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Init(&amp;Md5Ctx);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, HA1, HASHLEN);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, &quot;:&quot;, 1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, pszNonce, strlen(pszNonce));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, &quot;:&quot;, 1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, pszCNonce, strlen(pszCNonce));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Final(HA1, &amp;Md5Ctx);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CvtHex(HA1, SessionKey);<br/>};<br/><br/>/* calculate request-digest/response-digest as per HTTP Digest spec */<br/>void DigestCalcResponse(<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN HASHHEX HA1,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* H(A1) */<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszNonce,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* nonce from server */<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszNonceCount,&nbsp;&nbsp;/* 8 hex digits */<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszCNonce,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/* client nonce */<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszQop,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* qop-value: &quot;&quot;, &quot;auth&quot;, &quot;auth-int&quot; */<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszMethod,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/* method from the request */<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN char * pszDigestUri,&nbsp;&nbsp; /* requested URL */<br/>&nbsp;&nbsp;&nbsp;&nbsp;IN HASHHEX HEntity,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* H(entity body) if qop=&quot;auth-int&quot; */<br/>&nbsp;&nbsp;&nbsp;&nbsp;OUT HASHHEX Response&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/* request-digest or response-digest */<br/>&nbsp;&nbsp;&nbsp;&nbsp;)<br/>{<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5_CTX Md5Ctx;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HASH HA2;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HASH RespHash;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HASHHEX HA2Hex;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// calculate H(A2)<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Init(&amp;Md5Ctx);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, pszMethod, strlen(pszMethod));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, &quot;:&quot;, 1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, pszDigestUri, strlen(pszDigestUri));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (stricmp(pszQop, &quot;auth-int&quot;) == 0) {<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, &quot;:&quot;, 1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, HEntity, HASHHEXLEN);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Final(HA2, &amp;Md5Ctx);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CvtHex(HA2, HA2Hex);<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// calculate response<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Init(&amp;Md5Ctx);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, HA1, HASHHEXLEN);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, &quot;:&quot;, 1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, pszNonce, strlen(pszNonce));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, &quot;:&quot;, 1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (*pszQop) {<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, pszNonceCount, strlen(pszNonceCount));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, &quot;:&quot;, 1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, pszCNonce, strlen(pszCNonce));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, &quot;:&quot;, 1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, pszQop, strlen(pszQop));<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, &quot;:&quot;, 1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;};<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Update(&amp;Md5Ctx, HA2Hex, HASHHEXLEN);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MD5Final(RespHash, &amp;Md5Ctx);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CvtHex(RespHash, Response);<br/>};<br/><br/>File &quot;digtest.c&quot;:<br/><br/>#include &lt;stdio.h&gt;<br/>#include &quot;digcalc.h&quot;<br/><br/>void main(int argc, char ** argv) {<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char * pszNonce = &quot;dcd98b7102dd2f0e8b11d0f600bfb0c093&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char * pszCNonce = &quot;0a4f113b&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char * pszUser = &quot;Mufasa&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char * pszRealm = &quot;testrealm@host.com&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char * pszPass = &quot;Circle Of Life&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char * pszAlg = &quot;md5&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char szNonceCount[9] = &quot;00000001&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char * pszMethod = &quot;GET&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char * pszQop = &quot;auth&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char * pszURI = &quot;/dir/index.html&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HASHHEX HA1;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HASHHEX HA2 = &quot;&quot;;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HASHHEX Response;<br/><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DigestCalcHA1(pszAlg, pszUser, pszRealm, pszPass, pszNonce,<br/>pszCNonce, HA1);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DigestCalcResponse(HA1, pszNonce, szNonceCount, pszCNonce, pszQop,<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pszMethod, pszURI, HA2, Response);<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(&quot;Response = %s&#92;n&quot;, Response);<br/>};<br/><br/>到这里,关于使用SMTP发送mail就结束了, 由于水平有限, 有很多地方可能讲不够透彻!!!<br/><br/><br/>上面这个牛人这么牛逼但是Sendmail提示身份验证失败原因是犯下saslauthd服务没有开启的错误，也转载一下，呵呵：<br/><br/>问题描述: SendMail安装成功并已启动，利用foxmail可以收发Mail, 只是当选中“SMTP服务器需要身份验证”是，发送mail总是验证失败, 使用<br/>telnet登陆smtp服务器,输入ehlo ip返回信息如下:<br/>250-ENHANCEDSTATUSCODES<br/>250-PIPELINING<br/>250-8BITMIME<br/>250-SIZE<br/>250-DSN<br/>250-ETRN<br/>250-AUTH DIGEST-MD5 CRAM-MD5 LOGIN PLAIN<br/>250-DELIVERBY<br/>250 HELP<br/>从返回信息上可以SMTP服务器是可以通过DIGEST-MD5,CRAM-MD5,LOGIN PLAIN这几种方式验证的，在foxmail的帮助文档中也可以找到它<br/>是支持这些验证方式的,但是我输入:<br/>auth login&#92;r&#92;n<br/>334 VXNlcm5hbWU6&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//响应正确的<br/>Y2FydmVu&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//编码过的用户名<br/>334 UGFzc3dvcmQ6&nbsp;&nbsp;&nbsp;&nbsp;//返回也没问题<br/>Y2FydmVuMTIz&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//编码过的蜜码<br/>535 5.7.0 authentication failed&nbsp;&nbsp;&nbsp;&nbsp;//这里就出问题了<br/><br/>用这密码登陆自己的SMTP服务器是没问题的啊，終于在网上看到一篇讲sendmail认证的文章<br/><br/>(http://www.wangchao.net.cn/bbsdetail_1417288.html)，对sendmail作了如下改动:<br/>&nbsp;&nbsp;&nbsp;&nbsp;修改/etc/mail/sendmail.mc文件：<br/>　　&nbsp;&nbsp;&nbsp;&nbsp;第42行和43行，把最前面的dnl删除，变成:<br/>　　&nbsp;&nbsp;&nbsp;&nbsp;TRUST_AUTH_MECH(`EXTERNAL DIGEST-MD5 CRAM-MD5 LOGIN PLAIN&#039;)dnl<br/>　　&nbsp;&nbsp;&nbsp;&nbsp;define(`confAUTH_MECHANISMS&#039;, `EXTERNAL GSSAPI DIGEST-MD5 CRAM-MD5 LOGIN PLAIN&#039;)dnl<br/>　　&nbsp;&nbsp;&nbsp;&nbsp;第84 行DAEMON_OPTIONS(`Port=smtp,Addr=127.0.0.1, Name=MTA&#039;)dnl把里面的127.0.0.1改成0.0.0.0，<br/>&nbsp;&nbsp;&nbsp;&nbsp;把mc文件编译成sendmail的配置,运行m4 sendmail.mc &gt; sendmail.cf;<br/>　　/etc/init.d/sendmail restart（重新启动sendmail)<br/>然后用telnet试了下,发现还是同样的问题.<br/>难道还有什么没配置好?<br/><br/>再仔细看看这篇文，上面还说到了需要smtpd.conf和启动/etc/init.d/saslauthd, 第一次看也没注意到这两个问题，反正还没弄好，也<br/><br/>就试试吧，用ls /usr/lib/sasl2发现Sendmail.conf和smtpd.conf都存在,且内容也是 pwcheck_method:saslauthd.<br/>不会是saslauthd服务没有启动吧？<br/>chkconfig --list &#124; grep &quot;saslauthd&quot;<br/>saslauthd服务还真没有启动，由于sendmail不是我配置的，本人对于sendmail也不熟，也不知道需要些什么服务，只有照着网上说的做<br/><br/>了。<br/><br/>telnet测试如下:<br/>auth login<br/>334 VXNlcm5hbWU6<br/>Y2FydmVu<br/>334 UGFzc3dvcmQ6<br/>Y2FydmVuMTIz<br/>235 2.0.0 OK Authenticated<br/><br/>用foxmail发送也不再有问题了, saslauthd服务没有开启,害得我弄了老半天!!!<br/><br/>
]]>
</description>
</item><item>
<link>http://jackxiang.com/post//#blogcomment</link>
<title><![CDATA[[评论] 利用SMTP发送Mail详解]]></title> 
<author> &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> 
<guid>http://jackxiang.com/post//#blogcomment</guid> 
<description>
<![CDATA[ 
	
]]>
</description>
</item>
</channel>
</rss>