⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 qmail-popup.c

📁 linux下qmail的源码 本人加了一些注释
💻 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 + -