SMTP服务器编写示范Word文档格式.docx
- 文档编号:20590231
- 上传时间:2023-01-24
- 格式:DOCX
- 页数:15
- 大小:1.33MB
SMTP服务器编写示范Word文档格式.docx
《SMTP服务器编写示范Word文档格式.docx》由会员分享,可在线阅读,更多相关《SMTP服务器编写示范Word文档格式.docx(15页珍藏版)》请在冰豆网上搜索。
servaddr.sin_port=htons(25);
bind(1istenfd,(structsockaddr*)&
servaddr,sizeof(servaddr));
listen(1istenfd,5);
for(;
;
){
clilen=sizeof(cliaddr);
connfd=accept(1istenfd,
(structsockaddr*)&
cliaddr,&
clilen);
if(connfd<
=0){
break;
}
handleConnection(connfd);
close(connfd);
close(1istenfd);
连接一旦接受,新的客户机套接字将传递给handleconnection,以便实施SMTP服务器端协议的活动。
操作完成之后,该套接字关闭,并开始等待和处理下一个连接。
这个实现是每次处理一个连接的单线程设计。
这并不意味着在具体连接正在处理时(通过handleConnection),其他连接就被拒绝。
基于套接字API,同时发生的连接简单进行排队,最高可以通过listen调用5个连接。
这种形式的设计是有优点的,因为这是串行处理进入的邮件。
因为每次可以处理一个连接,所以不需要担心存在E-mail处理时的同步问题。
连接柄
连接柄是根据被连接的套接字的main得到的名称,是基本SMTP服务器协议。
首先初始化收到的E-mail工作拷贝的邮件结构。
这个结构可以传递到后面的功能,以便执行任何所需行动。
工作服务器邮件结构
#defineMAX_MAIL(1024*1024)
#defineMAX_ATTACHMENT(900*1024)
typedefstruct{
unsignedcharsender[80];
发送者
unsignedcharrecipient[80];
接受者
unsignedcharsubject[80];
主题字
unsignedcharfilename[80];
附件文件名
unsignedcharrawMail[MAX_MAIL];
原始邮件
unsignedcharattachment[MAX_ATTACHMENT];
intattachlen;
)Mail;
rawMail字符数组包括了通过连接接收到的原始邮件数据。
字段sender、subject、recipient、filename和attachment全部解码自rawMail。
数组大小可以根据实际情况进行更新。
Attachment的大小一般应该小于rawMail(因为解码过程会把Base64编码文本的4个字节转变成3个)。
大小超过限制,简单的返回错误,忽略该邮件。
SMTP协议处理—handleConnection()
handleConnection实现了SMTP锁步协议。
在客户机连接进来的时候,服务器发送致意220,并等待客户的致意响应HELO。
然后,服务器处理下一步协议元素MAILFROM、RCPTTO并给出相应回复。
接收到客户机的DATA命令后会准备接收E-mail报体。
报体的接收一直持续到空行和“.”的出现。
在接收过程中要分析每一个进来的字符。
然后等待客户的QUIT命令,结束SMTP连接。
然后,转移到邮件分析parseMail。
如下所示,流程见图5-4。
Constchar*salutation={“220EmbededHomeMailUpdate\n”};
Constchar*conclose={“221closingconnection\n”};
Constchar*goahead={“250OK\n”};
Constchar*gimme={“354Gimme!
\n”};
Constchar*closeit={“Serviceclosingtransmissionchannel.\n”};
voidhandleConnection(intfd)
intbufIdx=0;
intstate=0,len,i,stop=0;
charbuffer[81];
memset((void*)&
mail,0,sizeof(mail));
/*Indicatereadytogo…*/
len=write(fd,salutation,strlen(salutation));
/*Fixthenmapproblem*/|
if(len<
return;
}
/*Waitforsalutationresponse...*/
len=read(fd.buffer,255);
buffer[1en]=0;
if((strncmp(buffer,”HELO”,4))&
&
(strncmp(buffer,”EHLO”,4))){
closeConnection(fd);
write(fd,goahead,strlen(goahead));
/*WaitforMailFrom:
*/
len=read{fd,buffer,255};
buffer[1en]=0;
if(Strncmp(buffer,“MAILFROM”,9))
{closeConnection(fd);
grabAddress(buffer,mail.sender,256);
if(mail.sender[0]==0]{closeConnection(fd);
return;
)
write(fd,goahead,strlen(goahead));
/*WaitforRcptTo:
len=read(fd,buffer,255);
if(Strncmp(buffer,“RCPTTO”,7))
{closeConnection(fd);
return;
grabAddress(buffer,mail.recipient,256);
if(mail.recipient[0]==0){closeConnection(fd);
/*WaitforDATA*/
if(strncmp(buffer,”DATA”,4)){closeconnection(fd);
write(fd,gimme,strlen(gimme));
/*Looptoco11ectalloftheemailbody*/
bufIdx=state=stop=0;
while(!
stop){
if(bufIdx>
MAX_MAIL-80){closeConnection(fd);
return;
1er=read(fd,&
mail.rawMail[bufIdx],(MAX_MAIL-bufIdx));
if((len〈=0)||(bufIdx+len〉MAX_MAIL)){
retuurn;
------------------------------------------------------------------------
/*Searchfortheendinthecurrentbuffer*/
for(i=bufIdx;
i<
bufIdx+len;
i++){
if((state==0)&
&
(mail.rawMail[m]==0x0d))state=1;
elseif((state==1)&
(mail.rawMail[m]==0x0a))state=2;
elseif((state==2)&
(mail.rawMail[m]==’.’))state=3;
elseif((state==3)&
(mail.rawMail[m]==0x0d))state=4;
elseif((state==4)&
(mail.rawMail[m]==0x0a))
{stop=1;
break;
elsestate=0;
bufIdx+=len;
write(fd,goahead,strlen(goahead));
state=0;
while(state++〈10)
//WaitforQUIT
len=read(fd,buffer,80);
buffer[len]=0;
if(strncmp(buffer,"
QUTI"
4)){
closeconnection(fd);
}elsebreak;
/*Wenowhaveanemailinthemailstructure,trynowtoparseit.
*/
parseMail(&
mail,bufIdx);
write(fd,closeit,strlen(closeit));
连接柄提供了基本SMTP服务器实现。
我们先发送致意〈通知客户机它已经到达一个有效的SMTP服务器,准备接收报文〉。
接下来,等待来自客户机的致意响应。
然后,服务器处理下一个协议元素,根据发送E-mail的情况(通过MAILFROM命令)和E-maiI的去向〈通过RCPT〉进行构造。
然后DATA命令会等待正在准备接收的E-maA1报体接收报体是过程最简单的部分。
可以简单地缓冲所有收到的字符,直到找到只有字符“.”的一行。
为了性能原因,应该尽可能从套接字中进行读(直到原始缓冲的最大可用空间〉,然后在这个缓冲部分中查找单独符号"
."
行。
在这个循环中非常简单的状态机就可以提供这个功能。
一旦发现E-maAl结束指示符,将确认收到E-mail,然后等待客户机的QUE命令。
在服务器的这个点上,已经得到了包含在mail的rawMail结构中的E-mail,以及根据E-mal包封进行分析的sender和recipient字段。
现在就可以转移mail结构(及其由bufIdx辩识的rawMail部分的长度〉到parseMail,以便执行进一步的分析和解码。
Email分析程序—parseMail()
邮件分析程序的用途是在E-mail中(从E-mailcontents的报头开始)查找主题字符串。
这个主题可以解释为E-mail的命令(或目的〉。
一旦发现了主题,分析程序便变成命令解码器,然后基于发现的命令执行活动。
parseMail查找E-mail中的主题。
通过在E-mail报体中查找字符串Subject来发现E-mail的主题。
如无相应主题,返回-1,将邮件丢弃。
然后使用getAttachment来得到附件。
E-mail分析程序
intparseMail(parseMailType*mail,intlen)
inti=0,index;
intextractAttachment(parseMailType*,int,int);
/*First,grabthesubjectwhichisthelocationforthefile*/
for(i=0;
i<
len;
if(mail->
rawMail[i]=='
S’){//parseSubject
if(!
strncmp(&
mail->
rawMail[i],"
Subject:
"
8)){
i+=9;
index=0;
while((mail->
rawMail[m]!
=0x0d&
(mail->
=0x0a)){
subject[index++]=mail->
rawMail[m++];
subject[index]=0;
break;
}
}
/*
if(!
strncmp(mail->
subject,“WEB”,3)||!
subject,“APP”,3)||!
subject,“CORE”,3))
getAttachment(mail,m,len);
elsereturn–1;
return0;
if(i==len){return(-1);
/*Thisisourparser.Welookatthesubjectofthereceived*emailandthendeterminehowtodealwiththeemailandits*contents.
if(!
subject,"
STATUS"
6)){
emitStatusResponse(mail);
}elseif(lstrncmp(mail->
UPDATE"
/*Extracttheattachmentfromtheemail*/
extractAttachment(mail,i,len);
/*Atthispoint,themailstructurewillhavetheattachment*storedintheattachmentarray.Theattachmentwillcontain*thelengthoftheattachment,or-1ifanerroroccured.*/
emitUpdateResponse(mail);
}else{
/*Don'
tunderstandthecommand,silentlyignore...*/
查找E-mail中的主题是在E-mail报体中查找字符串Subject:
的一个简单过程。
为了优化查找,我们寻找字符"
s"
一旦找到这个字符,再与Subject:
字符串进行完整的对比。
接着将主题后的字符串复制到邮件结构的Subject字段上,但是不包括后面跟着的回车和换行符号。
如果主题没有发现,会返回-1,表明在分析过程中遇到错误。
因为已经填写了subject,所以可以根据远程控制需要所提供的特定命令要求SMTP服务器完成相关的操作。
如:
WEB、Control、Core、STATUS、UPDATE等。
发送状态响应的简单功能
intemitStatusResponse(parseMailType*mail)
structmailHeaderresponse;
charbuffer[512];
memset(&
response,0,sizeof(structmailHeader));
/*Makethesenderthenewrecipient*/
strcpy(response.recipient,mail->
sender);
/*Acknowledgereceiptofthemessage*/
strcpy(response.subject,"
DeviceStatus"
);
strcpy(response.contentType,"
text/plain"
response.contents=buffer;
memset(response.contents,0,sizeof(buffer));
sprintf(response.contents,
"
Receivedstatusrequestfrom%s.\n\n"
mail->
sendMail(&
response);
return(0);
可以利用被分析E-mail的发送者,以此作为发送E-mail的接收者。
这时需要给出新E-mail的主题和内容类型(简单的text/plain〉,然后再给出应答字符串。
这些功能可以使用HTML标志,提供更丰富的状态数据显示。
最后,邮件使用sendMai1功能进行发送(做为SMTP的客户端发送)。
UPDAIE命令相对复杂一点,因为可以使用这个命令接收Base64格式的二进制附件。
这方面的应用之一是接收将被写到闪存中的软件更新。
参见如下:
extractAttachment。
提取Base64编码附件的功能
intextractAttachment(parseMailType*mail,inti,intlen)
intindex,status;
//Next,grabthefilename
for(;
raWMail[i]=='
f'
){
raWMail[i],"
filename="
9)){
i+=10;
while((mail->
raWMail[i]!
=OxOd)&
(mail->
=OxOa)){
if(mail->
raWMail[i]=="
)i++;
elsemail->
filename[index++]=mail->
raWMail[i++];
filename[index]=0;
if(i==len){
printf("
Couldn’tfindthefilename...\n"
);
return(-1);
printf("
Thefilenamewas[%s]\n"
filename);
/*Finally,findthestartoftheBase64encodeddataand
*decode...
/*We'
relookingforaCR/LFonablanklineafterthe
*filenamespecification(sincethisisintheattachment*boundary).
if((mail->
rawMail[i]==OxOd)&
rawMail[i+1]==OxOa)&
rawMail[i+2]--OxOd)&
rawMail[i+3]==OxOa)){
i+=4;
printf(“Couldn’tfindtheBase64MIMEsection...\n“);
return(-1);
/*Atthispoint,itistheindextothestartoftheBase64
*encodedsection,startthedecodingprocess...
*/
attachlen=
b64decode(&
rawMail[i],mail->
attachrnent);
//附件解码
return(mail->
attachlen?
0:
-1);
提取附件的第一个任务是查找filename=字符串,这也在内容部分上进行,在此可以发现数据。
同样,查找第一个字符,在发现了这个字符之后,将此与字符串进行比较。
一旦发现,可以复制filename到邮件结构中的filename中。
如果是在提供文件系统的嵌入式系统中进行操作的,就可以用以创建二进制文件。
如果没有发现filename,将在调用列表中返回一个错误指示。
在附件内容部分中的filename字符串之后,E-mail使用一个空行分隔Base64编码数据。
跳过这个空行,
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- SMTP 服务器 编写 示范