3.8.2 smtpd_chat_query和smtpd_chat_replay:与smtp客户端交互
2016-04-07 10:14
405 查看
VSTRING和VSTREAM用来支持postfix的上层逻辑。我们来看两个应用场景:第一个场景:smtpd.c利用smtpd_chat_query和smtpd_chat_replay函数与客户端通信。在smtpd.c中的smtp协议解析过程中,与客户端的通信在两个函数中完成:smtpd_chat_query和smtpd_chat_replay。也就是从客户端取得和回应一行。这两个函数为smtp会话过程封装了通信技术细节。它们通过逐步封装VSTING和VSTREAM实现。在协议解析过程中,我们主要关注于业务而非底层操作,VSTRING和VSTREAM可以为我们屏蔽底层操作细节。与smtp服务器通信的客户可能来自网络或控制台,但在协议解析中我们不必区分这些细节——VSTREAM已经帮我们封装了这些底层差别,他们都是“流”。我们来看一下这里的“层层封装”的过程:(1)smtpd_chat_query和smtpd_chat_replay调用smtp_get和smtp_fputs函数实现,同时提供自己的处理。/smtpd/smtpd_chat.c中的smtpd_chat_query和smtpd_chat_replay函数通过/global/smtp_stream.c中的smtp_get和smtp_fputs函数实现。/smtpd/smtpd_chat.c
124/*smtpd_chat_query-receiveandrecordanSMTPrequest*/
125
126voidsmtpd_chat_query(SMTPD_STATE*state)
127{
128intlast_char;
129
130/*
131*Wecan'tparseorstoreinputthatexceedsvar_line_limit,soweskip
132*overittoavoidlossofsynchronization.
133*/
134last_char=smtp_get(state->buffer,state->client,var_line_limit,
135SMTP_GET_FLAG_SKIP);
136smtp_chat_append(state,"In:",STR(state->buffer));
137if(last_char!='\n')
138msg_warn("%s:requestlongerthan%d:%.30s...",
139state->namaddr,var_line_limit,
140printable(STR(state->buffer),'?'));
141
142if(msg_verbose)
143msg_info("<%s:%s",state->namaddr,STR(state->buffer));
144}smtpd_chat_query的实现较为简单,调用smtp_get从流得到行,调用smtp_chat_append函数记录历史数据:/smtpd/smtpd_chat.c
107/*smtp_chat_append-appendrecordtoSMTPtransactionlog*/
108
109staticvoidsmtp_chat_append(SMTPD_STATE*state,char*direction,
110constchar*text)
111{
112char*line;
113
114if(state->notify_mask==0)
115return;
116
117if(state->history==0)
118state->history=argv_alloc(10);
119line=concatenate(direction,text,(char*)0);
120argv_add(state->history,line,(char*)0);
121myfree(line);
122}smtpd_chat_replay函数则较为复杂:/smtpd/smtp_chat.c146/*smtpd_chat_reply-format,sendandrecordanSMTPresponse*/147148voidsmtpd_chat_reply(SMTPD_STATE*state,constchar*format,...)149{156/*157*Slowdownclientsthatmakeerrors.Sleep-on-anythingslowsdown158*clientsthatmakeanexcessivenumberoferrorswithinasession.159*/160if(state->error_count>=var_smtpd_soft_erlim)161sleep(delay=var_smtpd_err_sleep);160变量var_smtpd_soft_erlim,即在smtpd.c中main函数中初始化的postfix参数smtpd_soft_error_limit,默认值为10由于网络上针对邮件系统的恶意客户端很多,所以postfix设置了很多保护自己安全的方式。smtpd_soft_error_limit和smtpd_hard_error_limit就是其中两个参数:如果客户端出错过多,表明客户端可能有问题或者有攻击倾向,如果出错数大于smtpd_soft_error_limit所设定的值,且不超过smtpd_hard_error_limit(默认20)的值,则服务器等待(sleep)smtp_error_sleep_time参数所定义的时间(默认一秒)。如果错误数大于smtpd_hard_error_limit的值,则切断连接。postfix没有简单粗暴的对客户端出错直接断开连接,所以用户通过telnet进行客户端会话时,如果出现输入错误,依然有重试命令的机会。这两个参数的效果可以在命令行模式的smtp会话中通过重复输入错误命令来查看,在默认参数下,重复10次输入错误命令后,可能用户感觉不到这个1秒的延迟,但重复输入20次错误命令后,服务器将断开客户连接。如果客户端违反postfix的ACL规则,将累加SMTPD_STATE->error_count。也就是smtpd_chat_reply函数不仅仅是会给出响应代码,它同时控制着服务器对客户端的反应行为。167if(*var_smtpd_rej_footer168&&(*(cp=STR(state->buffer))=='4'||*cp=='5'))169smtp_reply_footer(state->buffer,0,var_smtpd_rej_footer,170STR(smtpd_expand_filter),smtpd_expand_lookup,171(void*)state);167smtpd_reject_footer是postfix2.8新增的一个参数,用来在输出smtp出错信息的同时(响应码第一位为4或5时表示出错),输出一个“页脚”,也就是一些附加帮助信息。我们看一下官方文档上的例子:http://www.postfix.org/postconf.5.html#smtpd_reject_footer172173/*All5xxrepliesmusthavea5.xx.xxdetailcode.*/174for(cp=STR(state->buffer),end=cp+strlen(STR(state->buffer));;){175if(var_soft_bounce){176if(cp[0]=='5'){177cp[0]='4';178if(cp[4]=='5')179cp[4]='4';180}181}182/*Thisiswhyweusestrlen()aboveinsteadofVSTRING_LEN().*/183if((next=strstr(cp,"\r\n"))!=0){184*next=0;185if(next[2]!=0)186cp[3]='-';/*contactfooterkludge*/187else188next=end;/*striptrailing\r\n*/189}else{190next=end;191}192smtp_chat_append(state,"Out:",cp);193194if(msg_verbose)195msg_info(">%s:%s",state->namaddr,cp);196197smtp_fputs(cp,next-cp,state->client);198if(next<end)199cp=next+2;200else201break;202}203204/*205*FlushunsentoutputifnoI/Ohappenedforawhile.Thisavoids206*timeoutswithpipelinedSMTPsessionsthathavelotsofserver-side207*delays(tarpitdelaysorDNSlookupsforUCErestrictions).208*/209if(delay||time((time_t*)0)-vstream_ftime(state->client)>10)210vstream_fflush(state->client);211212/*213*Abortimmediatelyiftheconnectionisbroken.214*/215if(vstream_ftimeout(state->client))216vstream_longjmp(state->client,SMTP_ERR_TIME);217if(vstream_ferror(state->client))218vstream_longjmp(state->client,SMTP_ERR_EOF);219220/*221*Orderlydisconnectincaseof421or521reply.222*/223if(strncmp(STR(state->buffer),"421",3)==0224||strncmp(STR(state->buffer),"521",3)==0)225state->flags|=SMTPD_FLAG_HANGUP;226}
/etc/postfix/main.cf:
smtpd_reject_footer=\c.Forassistance,call800-555-0101.
Pleaseprovidethefollowinginformationinyourproblemreport:
time($localtime),client($client_address)andserver
($server_name).Serverresponse:
550-5.5.1<user@example>Recipientaddressrejected:User
unknown.Forassistance,call800-555-0101.Pleaseprovidethe
followinginformationinyourproblemreport:time(Jan415:42:00),
client(192.168.1.248)andserver(mail1.example.com).
175var_soft_bounce即参数soft_bounce,postfix使用此参数测试内部ACL限制的生效情况。以5开头的smtp响应码为不可恢复错误,以4开头的为可恢复错误。设置soft_bounce=yes后,将把以5开头的smtp响应码改为以4开头,以方便测试ACL限制条件。
192记录响应历史数据。
197调用smtp_fputs向客户端返回一行。
215-218如果出现SMTP_ERR_TIME或SMTP_ERR_EOF错误,使用siglongjmp跳到出错处理语句执行。这也是处理smtp会话中错误的一处例子。
223-225对421(服务未就绪或临时禁止远端ip连接)和521(拒绝远端ip访问)错误设置SMTPD_FLAG_HANGUP标记,切断连接。接着逐级封装如下:(2)smtp_get和smtp_fputs函数调用vstring_get和vstring_fputs实现,同时提供了换行符(CRLF)的处理。(3)vstring_get和vstring_fput实现了真正的从流中取出行的操作。(4)vstring_get和vstring_fput的操作最终在其VBUF里实现。
相关文章推荐
- [na][tools]快速ping网段工具-QuickPing
- [转]MyBatis传入多个参数的问题 - mingyue1818
- [转]MyBatis传入多个参数的问题 - mingyue1818
- UIViewContentMode各类型效果
- 【Tips】【UE】总结自己常用的UltraEdit使用技巧
- CppCMS1.0.3 Build by VS2012
- 如何在Android Studio里设置一个项目所用的Build Tool版本?
- 干货技巧!有哪些实用秘诀可以帮助UI/UX设计师自我提升
- 3.7.4 event_request_timer:安排定时事件
- iOS 系统自带UItableviewcell上添加其它控件 不需要自定义cell 简单粗暴
- EASYUI 1.4版 combobox firefox 下不支持中文检索的问题
- UIView层次管理bringSubviewToFront,sendSubviewToBack
- 求第k个排列组合(Permutation Sequence)
- js数组实际上是key-value对,长度可无限增长
- 《iOS Human Interface Guidelines》——Text Field
- Information Acquisition
- $(".className").attr("disabled", "true")
- 分布式Unique ID的生成方法
- UIWindow简单介绍
- c#利用WebClient和WebRequest获取网页源代码的比较