📄 qmail-popup.c
字号:
//qmail-popup也是由tcp-env程序启动,tcp-env是通过管道与qmail-popup通信的.
//这也是qmail的美妙之处,总观整个qmail源代码,除少量dns代码外。基本上没有使用
//网络编程,各个进程间大部分都是管道通信,把监听,读写网络部分交给inetd或
//tcpserver来做,使得qmail代码相当容易阅读理解!
/*主要功能:
1.从网络读pop3命令,进行相应处理。
2.调用子进程(vchkpw或checkpassword,具体是哪一个由你在运行参数中指定,当然,
仔细分析完doanddie函数后也许就能编写自己的checkpw了)完成检验密码,启动
qmail-pop3d的工作
重要的函数是doanddie,理解这个函数基本上就能理解qmail pop密码的检验流程
几个程序间的关系是:
代码:
tcpserver--->qmail-popup----->vchkpw-----认证成功----->qmail-pop3d
| |
| |
<------认证失败------
*/
#include "commands.h"#include "fd.h"#include "sig.h"#include "stralloc.h"#include "substdio.h"#include "alloc.h"#include "wait.h"#include "str.h"#include "byte.h"#include "now.h"#include "fmt.h"#include "exit.h"#include "readwrite.h"#include "timeoutread.h"#include "timeoutwrite.h"void die() { _exit(1); }int saferead(fd,buf,len) int fd; char *buf; int len;{ int r; r = timeoutread(1200,fd,buf,len); if (r <= 0) die(); return r;}int safewrite(fd,buf,len) int fd; char *buf; int len;{ int r; r = timeoutwrite(1200,fd,buf,len); if (r <= 0) die(); return r;}char ssoutbuf[128];substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);char ssinbuf[128];substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);void puts(s) char *s;{ substdio_puts(&ssout,s);}void flush(){ substdio_flush(&ssout);}void err(s) char *s;{ puts("-ERR "); puts(s); puts("\r\n"); flush();}void die_usage() { err("usage: popup hostname subprogram"); die(); }void die_nomem() { err("out of memory"); die(); }void die_pipe() { err("unable to open pipe"); die(); }void die_write() { err("unable to write pipe"); die(); }void die_fork() { err("unable to fork"); die(); }void die_childcrashed() { err("aack, child crashed"); }void die_badauth() { err("authorization failed"); }void err_syntax() { err("syntax error"); }void err_wantuser() { err("USER first"); }void err_authoriz() { err("authorization first"); }void okay() { puts("+OK \r\n"); flush(); }void pop3_quit() { okay(); die(); }char unique[FMT_ULONG + FMT_ULONG + 3];char *hostname;stralloc username = {0};int seenuser = 0;char **childargs;substdio ssup;char upbuf[128];void doanddie(user,userlen,pass)char *user;unsigned int userlen; /* including 0 byte */char *pass;{ int child; int wstat; int pi[2]; if (fd_copy(2,1) == -1) die_pipe(); //关闭出错(fd2),将标准输出(fd1),定向到标准出错(fd2) close(3); if (pipe(pi) == -1) die_pipe(); if (pi[0] != 3) die_pipe(); //确保向子进程能够读到硬编码的fd3
//建立子进程执行subprogram给出的程序,一般是一个检验用户名和密码的程序 switch(child = fork()) { case -1: die_fork(); case 0: close(pi[1]);
//子进程执行checkpassword或vchkpw之类的程序,检验密码,如果认证通过 sig_pipedefault(); execvp(*childargs,childargs); //这些再调用qmail-pop3d _exit(1); }
//父进程向子进程的fd3传送用户名及密码,这是一个约定,如果你要自己的检验密码的程序,
//记得从fd3读密码哦。 close(pi[0]); substdio_fdbuf(&ssup,write,pi[1],upbuf,sizeof upbuf); if (substdio_put(&ssup,user,userlen) == -1) die_write(); if (substdio_put(&ssup,pass,str_len(pass) + 1) == -1) die_write();
//父进程向子进程传送<进程id,当前时间@主机名> if (substdio_puts(&ssup,"<") == -1) die_write(); if (substdio_puts(&ssup,unique) == -1) die_write(); if (substdio_puts(&ssup,hostname) == -1) die_write(); if (substdio_put(&ssup,">",2) == -1) die_write(); if (substdio_flush(&ssup) == -1) die_write(); close(pi[1]);
//清除密码及用户名缓冲区 byte_zero(pass,str_len(pass)); byte_zero(upbuf,sizeof upbuf); if (wait_pid(&wstat,child) == -1) die(); //等待子进程结束 if (wait_crashed(wstat)) die_childcrashed(); if (wait_exitcode(wstat)) die_badauth();
//完成一次pop3对话退出 die();}
//显示欢迎信息void pop3_greet(){ char *s; s = unique; s += fmt_uint(s,getpid()); *s++ = '.'; s += fmt_ulong(s,(unsigned long) now()); *s++ = '@'; *s++ = 0; puts("+OK <"); puts(unique); puts(hostname); puts(">\r\n"); flush();}
//设置标志,初始化用户名变量void pop3_user(arg) char *arg;{ if (!*arg) { err_syntax(); return; } okay(); seenuser = 1; //user命令已经执行的标志 if (!stralloc_copys(&username,arg)) die_nomem(); //将参数存入username if (!stralloc_0(&username)) die_nomem(); }void pop3_pass(arg) char *arg;{ if (!seenuser) { err_wantuser(); return; } //如果没有执行user命令,返回 if (!*arg) { err_syntax(); return; } doanddie(username.s,username.len,arg); //调用子进程验证密码并等待它完成}
//用户名及密码在一个命令中给出的情况,见user,passvoid pop3_apop(arg) char *arg;{ char *space; space = arg + str_chr(arg,' '); if (!*space) { err_syntax(); return; } *space++ = 0; doanddie(arg,space - arg,space);}
//命令及相应的处理函数表struct commands pop3commands[] = { { "user", pop3_user, 0 }, { "pass", pop3_pass, 0 }, { "apop", pop3_apop, 0 }, { "quit", pop3_quit, 0 }, { "noop", okay, 0 }, { 0, err_authoriz, 0 }} ;void main(argc,argv)int argc;char **argv;{ sig_alarmcatch(die); //捕获sigalrm信号 sig_pipeignore(); //忽略pipe信号 hostname = argv[1]; //hostname指向程序的第一个参数 if (!hostname) die_usage(); childargs = argv + 2; if (!*childargs) die_usage(); pop3_greet(); //显示欢迎信息后进入命令循环,等待用户命令 commands(&ssin,pop3commands); die();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -