这几天敝人并没有及时更新博客,一是由于白天工作中的锁事比较繁杂,二来连续看了两个晚上的SMTP协议。
我比较喜欢用实践来证明一切,这样才会加深自己的理解!
一、准备工作:
1、本机环境:Windows XP SP3、ADSL 10M光纤
2、开发工具:WildPackets OmniPeek V5.1.4
Visual C++ 6.0
Outlook Express6.0
FlexEdit V2.3.1871
二、SMTP命令:
1、HELO 向服务器标识用户身份
2、MAIL 初始化邮件传输mail from: <xxx>
3、RCPT 标识单个的邮件接收人;常在MAIL命令后面可有多个rcpt to: <xxx>
4、DATA 在单个或多个RCPT命令后,表示所有的邮件接收人已标识,初始化数据传输,以.结束
5、NOOP 无操作,服务器应响应OK
6、RSET 重置会话,当前传输被取消
7、QUIT 结束会话
三、分析数据包:
1、打开Outlook Express6.0,创建新邮件,内容如下:
2、打开OmniPeek,选择SMTP:
3、先开始抓包,再发送上面的测试邮件,得到的数据包如下图:
(要注意Source与Destination,即源地址与目标地址)
4、只需要看第20条数据包,其他可以比较容易理解:
5、其他小技巧
<CR> <LF>即对应C/C++中的"/r /n"
比如 Line 8: charset="gb2312"<CR><LF>,因为需要用到转义字符
所以对应的字符串应该为char * sData = "charset=/"gb2312/"/r/n";
四、相关代码:
1、以下代码通过socket与SMTP服务器建立连接并验证身份。打开Visual C++,新建一个控制台工程、并添加一个CPP文件:
- /************************************************************************/
- /* main.cpp SMTP协议学习笔记 - 登录验证
- /* by Koma 2009.9.10 11:35
- /* http://blog.csdn.net/wangningyu
- /************************************************************************/
- #include "stdio.h"
- #include "ZBase64.h"
- #include "winsock2.h"
- #pragma comment(lib,"ws2_32.lib")
- int main(int argc, char* argv[])
- {
- SOCKADDR_IN saServer;
- LPHOSTENT lphostent;
- WSADATA wsadata;
- SOCKET hsocket;
- int nRet;
- char* host_name="smtp.tom.com";
- char* req=
- // 每发送一行数据服务器都会作出响应
- "EHLO KOMAWANG/r/n"
- "AUTH LOGIN/r/n"
- // 两行是登录用户与密码(采用Base64加密)
- "bm**********20=/r/n"
- "d2******W4=/r/n"
- "QUIT/r/n";
- // 初始化套接字
- if(WSAStartup(MAKEWORD(2,2),&wsadata))
- printf("初始化SOCKET出错!");
- // SMTP端口默认是25
- lphostent=gethostbyname(host_name);
- if(lphostent==NULL)
- printf("lphostent为空!");
- hsocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
- saServer.sin_family = AF_INET;
- saServer.sin_port = htons(25);
- saServer.sin_addr = *((LPIN_ADDR)*lphostent->h_addr_list);
- // 利用SOCKET连接
- nRet = connect(hsocket,(LPSOCKADDR)&saServer,sizeof(SOCKADDR_IN));
- if(nRet == SOCKET_ERROR)
- {
- printf("建立连接时出错!/n");
- closesocket(hsocket);
- return 0;
- }
- // 利用SOCKET发送
- nRet = send(hsocket,req,strlen(req),0);
- if(nRet==SOCKET_ERROR)
- {
- printf("发送数据包时出错!");
- closesocket(hsocket);
- }
- char Dest[3000];
- memset(Dest,0,3000);
- nRet=1;
- while(nRet>0)
- {
- // 接收返回数据包
- nRet=recv(hsocket,(LPSTR)Dest,sizeof(Dest),0);
- if(nRet>0)
- Dest[nRet]=0;
- else
- Dest[0]=0;
- // 显示返回数据包的大小、内容
- printf("/n返回数据包大小:%d/n",nRet);
- printf("返回数据包内容:/n%s",Dest);
- }
- return 0;
- }
2、下面是Base64加解密头与CPP文件:
- /************************************************************************/
- /* ZBase64.h
- /************************************************************************/
- #ifndef _ZBASE64
- #define _ZBASE64
- #pragma warning(disable:4786)
- #include <string>
- using namespace std;
- class ZBase64
- {
- public:
- /*编码
- DataByte
- [in]输入的数据长度,以字节为单位
- */
- string Encode(const unsigned char* Data,int DataByte);
- /*解码
- DataByte
- [in]输入的数据长度,以字节为单位
- OutByte
- [out]输出的数据长度,以字节为单位,请不要通过返回值计算
- 输出数据的长度
- */
- string Decode(const char* Data,int DataByte,int& OutByte);
- };
- #endif
- /************************************************************************/
- /* ZBase64.cpp
- /************************************************************************/
- #include "ZBase64.h"
- string ZBase64::Encode(const unsigned char* Data,int DataByte)
- {
- //编码表
- const char EncodeTable[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- //返回值
- string strEncode;
- unsigned char Tmp[3]={0};
- int LineLength=0;
- for(int i=0;i<(int)(DataByte / 3);i++)
- {
- Tmp[1] = *Data++;
- Tmp[2] = *Data++;
- Tmp[3] = *Data++;
- strEncode+= EncodeTable[Tmp[1] >> 2];
- strEncode+= EncodeTable[((Tmp[1] << 4) | (Tmp[2] >> 4)) & 0x3F];
- strEncode+= EncodeTable[((Tmp[2] << 2) | (Tmp[3] >> 6)) & 0x3F];
- strEncode+= EncodeTable[Tmp[3] & 0x3F];
- if(LineLength+=4,LineLength==76) {strEncode+="/r/n";LineLength=0;}
- }
- //对剩余数据进行编码
- int Mod=DataByte % 3;
- if(Mod==1)
- {
- Tmp[1] = *Data++;
- strEncode+= EncodeTable[(Tmp[1] & 0xFC) >> 2];
- strEncode+= EncodeTable[((Tmp[1] & 0x03) << 4)];
- strEncode+= "==";
- }
- else if(Mod==2)
- {
- Tmp[1] = *Data++;
- Tmp[2] = *Data++;
- strEncode+= EncodeTable[(Tmp[1] & 0xFC) >> 2];
- strEncode+= EncodeTable[((Tmp[1] & 0x03) << 4) | ((Tmp[2] & 0xF0) >> 4)];
- strEncode+= EncodeTable[((Tmp[2] & 0x0F) << 2)];
- strEncode+= "=";
- }
- return strEncode;
- }
- string ZBase64::Decode(const char* Data,int DataByte,int& OutByte)
- {
- //解码表
- const char DecodeTable[] =
- {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 62, // '+'
- 0, 0, 0,
- 63, // '/'
- 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // '0'-'9'
- 0, 0, 0, 0, 0, 0, 0,
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
- 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 'A'-'Z'
- 0, 0, 0, 0, 0, 0,
- 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
- 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // 'a'-'z'
- };
- //返回值
- string strDecode;
- int nValue;
- int i= 0;
- while (i < DataByte)
- {
- if (*Data != '/r' && *Data!='/n')
- {
- nValue = DecodeTable[*Data++] << 18;
- nValue += DecodeTable[*Data++] << 12;
- strDecode+=(nValue & 0x00FF0000) >> 16;
- OutByte++;
- if (*Data != '=')
- {
- nValue += DecodeTable[*Data++] << 6;
- strDecode+=(nValue & 0x0000FF00) >> 8;
- OutByte++;
- if (*Data != '=')
- {
- nValue += DecodeTable[*Data++];
- strDecode+=nValue & 0x000000FF;
- OutByte++;
- }
- }
- i += 4;
- }
- else// 回车换行,跳过
- {
- Data++;
- i++;
- }
- }
- return strDecode;
- }
3、程序运行效果: