频道直达 - 专题 - 新闻 - 技巧 - 组网 - 开发 - 安全 - web编程 - 图像 - 操作系统 - 数据库 - 教育 - 旅游 - 健康 - 时尚 - 驱动 - 软件 - 游戏 - 多媒体 - ERP - 讨论组

程序员眼中的qmail(qmail源代码分析)

来源: 作者: 出处:巧巧读书 2006-09-02 进入讨论组
下一页 1 2 3 4 5 6 7 8 9 
访问地址 http://www.qqread.com/cpp/x206531.html

  很多人对qmail smtp的认证机制,环境变量,执行顺序不太了解。

  仔细看完这一大篇代码后相信你会明白很多你过去不太明白的问题。

  当然你要有一点点c语言基础。也只要一点点。

  Come from: ChongQing Gearbox co.,ltd

  这份文件还不完善,如果您完善了它请发一份给我: beggar110@163.com

  这份文件是给想深入了解qmail和想hacker qmail的人读的,如果你只是想建立一个能够运作的mail服务器,没有必要读下去了。它将浪费你很多的时间。

  如果你对qmail控制文件还不是很了解,阅读这份文件之前,请先阅读rainbow的《qmail控制文件详解》

  在这里你可以找到www.chinaunix.net/forum/viewtopic.php?t=1126

  好的。开始我们qmail内部的漫游吧!!!Let's go!

  代码:

  qmail 总览

  tcpserver MUA

  | |

  V V

  qmail-smtpd qmail-inject

  | |

  +----------->qmail-queue<-----------+

  |

  |

  qmail-send

  |

  +------------+------------+

  | |

  V V

  qmail-rspawn qmail-lspawn

  | |

  V V

  qmail-remote qmail-local

  | |

  | |

  V V

  INTERNET <----qmail-pop3d

  |

  |

  vchkpw

  |

  |

  qmail-popup

  |

  |

  tcpserver--+

  qmail-smtpd.c源代码分析(去掉了所有include)

  qmail -smtpd是由tcpserver或由tcp-env启动。tcpserver负责监听端口,如果指定了-x rule.cbd,tcpserver会先决断是断开连接还是启动qmail子进程。如果没有指定-x参数启动tcpserver,那么直接启动 qmail-smtpd.启动qmail-smtpd之前将来自网络的数据连接重定向到qmail-smtpd的fd0,fd1.还会初始化一些 qmail-smtpd需要的环境变量,如TCPREMOTEIP.

  tcp-env只会初始化qmail-smtpd的环境变量,不负责监听端口及重定向网络连接。所以tcp-env要和inetd配合使用。当然,由于初始化环境变量的工作tcpserver也会作,所以没有必要tcpserver和tcp-env配合使用.

  qmail-smtpd完成邮件smtp命令的接收,并调用相应的处理程序。

  检查mail 中的地址是否在control/badmailfrom中定义(MAIL命令)

  检查是否设置了RELAYCLIENT环境变量或 rcpt 中的地址是否是control/rcpthosts中定义(RCPT命令)

  需要明确的是qmail-smtpd只是简单的接收邮件内容传送给qmail-queue,并不对邮件进行转发(DATA命令)。

  当然还要向qmail-queue传送mailfrom,mailto

  代码:

  #define MAXHOPS 100

  unsigned int databytes = 0; //邮件最大长度:0=无限

  int timeout = 1200; //默认超时20分钟

  //向网络写,超时值为control/timeoutsmtpd指定的值。没有这个文件则取默认值20分钟

  int safewrite(fd,buf,len) int fd; char *buf; int len;

  {

  int r;

  r = timeoutwrite(timeout,fd,buf,len);

  if (r <= 0) _exit(1);

  return r;

  }

  char ssoutbuf[512];

  substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);

  void flush() { substdio_flush(&ssout); }

  void out(s) char *s; { substdio_puts(&ssout,s); }

  //错误处理函数

  void die_read() { _exit(1); }

  void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); }

  void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); }

  void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); }

  void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); }

  void straynewline() { out("451 See pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); }

  void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }

  void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }

  void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); }

  void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); }

  void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); }

  void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); }

  void err_noop() { out("250 ok\r\n"); }

  void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); }

  void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); }

  stralloc greeting = {0};

  //输出提示信息*code

  void smtp_greet(code) char *code;

  {

  substdio_puts(&ssout,code);

  substdio_put(&ssout,greeting.s,greeting.len);

  }

  void smtp_help()

  {

  out("214 qmail home page:

  void>pobox.com/~djb/qmail.html\r\n");

  }

  void smtp_quit()

  {

  smtp_greet("221 "); out("\r\n"); flush(); _exit(0);

  }

  char *remoteip; //远端ip地址

  char *remotehost; //远端主机名

  char *remoteinfo; //远端信息

  char *local; //本地主机

  char *relayclient; //是否检查rcpthosts文件

  stralloc helohost = {0};

  char *fakehelo; /* pointer into helohost, or 0 */

  void dohelo(arg) char *arg; {

  if (!stralloc_copys(&helohost,arg)) die_nomem();

  if (!stralloc_0(&helohost)) die_nomem();

  //fakehelo变量,如果helo 参数指定的主机名与TCPREMOTEHOST环境变量中的主机名不同则

  //fakehelo的值为helo命令的参数指定的主机名.如果两者相同则fekehelo为NULL;

  //data命令处理程式用到这个变量

  fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0;

  }

  int liphostok = 0;

  stralloc liphost = {0};

  int bmfok = 0;

  stralloc bmf = {0};

  struct constmap mapbmf;

  void setup()

  {

  char *x;

  unsigned long u;

  if (control_init() == -1) die_control(); //control/me

  //读入欢迎信息greeting,如果不存在则从me文件复制

  if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1)

  die_control();

  //读入localiphost,如果文件不存在则从me文件复制

  liphostok = control_rldef(&liphost,"control/localiphost",1,(char *) 0);

  if (liphostok == -1) die_control();

  //读control/timeoutsmtpd存入timeout,用于控制超时的情况.

  if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control();

  if (timeout <= 0) timeout = 1;

  if (rcpthosts_init() == -1) die_control();

  //读入badmailfrom文件存入 bmf

  bmfok = control_readfile(&bmf,"control/badmailfrom",0);

  if (bmfok == -1) die_control();

  if (bmfok)

  if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem();

  //读入databytes文件存入 databytes,如果该文件不存在,则将

  //databytes的值设为0.

  if (control_readint(&databytes,"control/databytes") == -1) die_control();

  x = env_get("DATABYTES");

  if (x) { scan_ulong(x,&u); databytes = u; }

  if (!(databytes + 1)) --databytes;

  //取tcp-environ环境变量,如果环境变量没有设置,将它的值设置为unknow.

  //这些信息来自tcpserver,或tcp-env之类的程式

  remoteip = env_get("TCPREMOTEIP");

  if (!remoteip) remoteip = "unknown";

  local = env_get("TCPLOCALHOST");

  if (!local) local = env_get("TCPLOCALIP");

  if (!local) local = "unknown";

  remotehost = env_get("TCPREMOTEHOST");

  if (!remotehost) remotehost = "unknown";

  remoteinfo = env_get("TCPREMOTEINFO");

  //从环境变量RELAYCLIENT读入.

  //如果RELAYCLIENT变量没有设置那么relayclient将会是NULL.

  relayclient = env_get("RELAYCLIENT");

  dohelo(remotehost);

  }

  stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */

  //对命令参数arg进行邮件地址分析

  //并将分离出的email地址存入全局缓存addr

  //成功返回值为1,失败返回0

  int addrparse(arg)

  char *arg;

  {

  int i;

  char ch;

  char terminator;

  struct ip_address ip;

  int flagesc;

  int flagquoted;

  //分离出邮件地址

  //例如: arg="",或 arg=": email@eg.org "

  //执行下面这段程式后arg="email@eg.org"

  terminator = '>';

  i = str_chr(arg,'<');

  if (arg[i])

  arg += i + 1;

  else { /* partner should go read rfc 821 */

  terminator = ' ';

  arg += str_chr(arg,':');

  if (*arg == ':') ++arg;

  while (*arg == ' ') ++arg;

  }

  /* strip source route */

  if (*arg == '@') while (*arg) if (*arg++ == ':') break;

  if (!stralloc_copys(&addr,"")) die_nomem();

  flagesc = 0;

  flagquoted = 0;

  for (i = 0;ch = arg[i];++i) { /* copy arg to addr, stripping quotes */

更多文章 更多内容请看其他电子邮件服务器DNS&Mail服务器专题,或进入讨论组讨论。
下一页 1 2 3 4 5 6 7 8 9 
收藏此文】【 】【打印】【关闭
相关图文阅读
频道图文推荐
健 康 咨 询
时 尚 咨 询
巧巧读书宗旨
相关专题
最新论坛文章
站内各频道最新更新文档
站内最新制作专题
热门关键字导读
Photoshop教 程照片处理 照片制作 PS快捷键 抠图
计 算 机 故 障XP系统修复
艺 术 与 设 计设计 流媒体 设计欣赏 边框
计 算 机 安 全ARP
站内频道文章精选
巧巧电脑频道编辑信箱  告诉我们您想看的专题或文章