13.6.3 与邮件服务器认证交互的过程
由于每种协议的交互过程都不相同,因此下面仅以POP3协议为例简单地说明这一过程是如何实现的,如下所示。
static void ngx_mail_proxy_pop3_handler(ngx_event_t*rev)
{
u_char*p;
ngx_int_t rc;
ngx_connection_t*c;
ngx_mail_session_t*s;
ngx_mail_proxy_conf_t*pcf;
//line将会保存发往上游邮件服务器的消息
ngx_str_t line;
//获取Nginx与上游间的连接
c=rev->data;
//获得ngx_mail_session_t结构体
s=c->data;
//如果读取上游邮件服务器响应超时,则向客户端发送错误响应
if(rev->timedout){
c->timedout=1;
ngx_mail_proxy_internal_server_error(s);
return;
}
//读取上游邮件服务器发来的响应到buffer缓冲区中
rc=ngx_mail_proxy_read_response(s,0);
//还需要继续接收邮件服务器的消息,期待下一次的调度
if(rc==NGX_AGAIN){
return;
}
//消息不合法,或者邮件服务器没有验证通过,则返回错误给客户端
if(rc==NGX_ERROR){
ngx_mail_proxy_upstream_error(s);
return;
}
switch(s->mail_state){
case ngx_pop3_start:
//构造发送给邮件服务器的用户信息
line.len=sizeof("USER")-1+s->login.len+2;
line.data=ngx_pnalloc(c->pool,line.len);
if(line.data==NULL){
ngx_mail_proxy_internal_server_error(s);
return;
}
p=ngx_cpymem(line.data,"USER",sizeof("USER")-1);
p=ngx_cpymem(p,s->login.data,s->login.len);
p++=CR;p=LF;
s->mail_state=ngx_pop3_user;
break;
case ngx_pop3_user:
//构造发送给邮件服务器的密码信息
line.len=sizeof("PASS")-1+s->passwd.len+2;
line.data=ngx_pnalloc(c->pool,line.len);
if(line.data==NULL){
ngx_mail_proxy_internal_server_error(s);
return;
}
p=ngx_cpymem(line.data,"PASS",sizeof("PASS")-1);
p=ngx_cpymem(p,s->passwd.data,s->passwd.len);
p++=CR;p=LF;
s->mail_state=ngx_pop3_passwd;
break;
case ngx_pop3_passwd:
/*在收到服务器返回的密码验证通过信息后,将Nginx与下游客户端间、Nginx与上游邮件服务器间的TCP连接上读/写事件的回调方法都设置为ngx_mail_proxy_handler方法(参见13.7节)
s->connection->read->handler=ngx_mail_proxy_handler;
s->connection->write->handler=ngx_mail_proxy_handler;
rev->handler=ngx_mail_proxy_handler;
c->write->handler=ngx_mail_proxy_handler;
……
//进入透传上、下游TCP阶段
ngx_mail_proxy_handler(s->connection->write);
return;
default:
if(NGX_SUPPRESS_WARN)
ngx_str_null(&line);
endif
break;
}
/向上游的邮件服务器发送验证信息。注意,这里向邮件服务器发送TCP流与本书的其他章节都不相同,它不再通过epoll检测到TCP连接上出现可写事件而触发。事实上,它是由连接上出现的可读事件触发的,因为读取到了邮件服务器的消息,才向邮件服务器发送消息。之所以可以这么做的一个原因在于,当前阶段发送的TCP消息包都非常短小/
if(c->send(c,line.data,line.len)<(ssize_t)line.len){
ngx_mail_proxy_internal_server_error(s);
return;
}
//清空buffer缓冲区
s->proxy->buffer->pos=s->proxy->buffer->start;
s->proxy->buffer->last=s->proxy->buffer->start;
}
一旦收到用户名、密码验证通过的消息,就会由ngx_mail_proxy_handler方法进入透传上、下游TCP流的阶段。