📄 qmail-smtpd.c
字号:
// qmail-smtpd接受远程主机的邮件并转交给队列处理程序qmail-queue来处理
// 由tcp-env.c启动,完成邮件smtp命令的接收,并调用相应的处理程序
// 只接收邮件内容(mailfrom,mailto)并传送给qmail-queue,并不对邮件进行转发
#include "sig.h"#include "readwrite.h"#include "stralloc.h"#include "substdio.h"#include "alloc.h"#include "auto_qmail.h"#include "control.h"#include "received.h"#include "constmap.h"#include "error.h"#include "ipme.h"#include "ip.h"#include "qmail.h"#include "str.h"#include "fmt.h"#include "scan.h"#include "byte.h"#include "case.h"#include "env.h"#include "now.h"#include "exit.h"#include "rcpthosts.h"#include "timeoutread.h"#include "timeoutwrite.h"#include "commands.h"#define MAXHOPS 100unsigned 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 相当于 void out(char* s)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 http://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};//end 错误处理函数
// 输出提示信息*codevoid smtp_greet(code) char *code;{ substdio_puts(&ssout,code); substdio_put(&ssout,greeting.s,greeting.len);}void smtp_help(){ out("214 qmail home page: http://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;
// badmailfrom 用来指定不喜欢的发见人的邮件地址或者是域名
//该地址给主机发送邮件时将得到code 553,告知其是不受欢迎的发件人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
//这些信息在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,
//失败返回0int 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 */ if (flagesc) { if (!stralloc_append(&addr,&ch)) die_nomem(); flagesc = 0; } else { if (!flagquoted && (ch == terminator)) break; switch(ch) { case '\\': flagesc = 1; break; case '"': flagquoted = !flagquoted; break; default: if (!stralloc_append(&addr,&ch)) die_nomem(); } } } /* could check for termination failure here, but why bother? */ if (!stralloc_append(&addr,"")) die_nomem();
//将ip地址转换为主机名:
//如test@[10.0.6.21]转换为test@host.mydomain.org
//依据是control/localiphost文件中有host.mydomain.org if (liphostok) { i = byte_rchr(addr.s,addr.len,'@'); if (i < addr.len) /* if not, partner should go read rfc 821 */ if (addr.s[i + 1] == '[') //比较是否是用[]括起来的ip地址 if (!addr.s[i + 1 + ip_scanbracket(addr.s + i + 1,&ip)]) if (ipme_is(&ip)) { addr.len = i + 1; if (!stralloc_cat(&addr,&liphost)) die_nomem(); if (!stralloc_0(&addr)) die_nomem(); } } if (addr.len > 900) return 0; //地址太长,出错返回 return 1; //成功返回}
//简单的垃圾邮件检查,检查全局缓冲区addr中的地址是否有在badmailfrom中定义,如果
//有则返回1,否则返回0.int bmfcheck(){ int j; if (!bmfok) return 0; if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1; j = byte_rchr(addr.s,addr.len,'@'); if (j < addr.len) if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1; return 0;}
//检查全局缓存addr中的邮件地址是否要进行转发(依据control/rcpthosts文件)
//可以进行转发返回1,拒绝转发返回0int addrallowed(){ int r;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -