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流的阶段。