📄 qmail-smtpd.c
字号:
r = rcpthosts(addr.s,str_len(addr.s)); if (r == -1) die_control(); return r;}int seenmail = 0;int flagbarf; /* defined if seenmail */stralloc mailfrom = {0};stralloc rcptto = {0};void smtp_helo(arg) char *arg;{ smtp_greet("250 "); out("\r\n"); seenmail = 0; dohelo(arg);}void smtp_ehlo(arg) char *arg;{ smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n"); seenmail = 0; dohelo(arg);}
//重新初始化,调用helo或ehlo命令都会完成相同的功能void smtp_rset(){ seenmail = 0; out("250 flushed\r\n");}
//mail命令解释程序,重要变量:[mailfrom/全局]
//该函数完成检查mailfrom是否在badmailfrom中定义,设置标志指明mail命令已经执行void smtp_mail(arg) char *arg;{ if (!addrparse(arg)) { err_syntax(); return; }
//检查是否badmailfrom,如果是设置相应标志,这个标志在rcpt命令的处理程序中才起作用 flagbarf = bmfcheck(); seenmail = 1; //指示已经执行过mail命令。 if (!stralloc_copys(&rcptto,"")) die_nomem(); //分配rcptto缓冲区 if (!stralloc_copys(&mailfrom,addr.s)) die_nomem(); //复制mail命令中指定的地址到mailfrom if (!stralloc_0(&mailfrom)) die_nomem(); out("250 ok\r\n");}
//rcpt命令解释程序,重要变量:[rcptto/全局]void smtp_rcpt(arg) char *arg; { if (!seenmail) { err_wantmail(); return; } //买了命令是否已经执行? if (!addrparse(arg)) { err_syntax(); return; } //分离邮件地址参数存入全局缓存addr
//如果mail命令中的地址在control/badmailfrom中有定义,返回
if (flagbarf) { err_bmf(); return; }
//至此addr缓存中包含了rcpt命令指定的email地址
//如果rcpt命令,则有addr="email@eg.org"。这个变量是在addrparse函数中赋值的
//如果RELAYCLIENT环境变量设置将不进行rcpthosts,morercpthosts.cdb的比较
//足以,大国smtp认证补丁,如果通过认证后会设置relayclient="" if (relayclient) { --addr.len; if (!stralloc_cats(&addr,relayclient)) die_nomem(); if (!stralloc_0(&addr)) die_nomem(); } else //如果没有指定RELAYCLIENT变量,则有control/rcpthosts决定是否进行转发 if (!addrallowed()) { err_nogateway(); return; }
//生成头连接到全局缓存rcptto:
//例如地址"rcpt test@eg.org"命令将产生rcptto="Temail@eg.org"
//多次执行rcpt命令效果会是rcptto=“Ttest@eg.orgTtwo@eg.org” if (!stralloc_cats(&rcptto,"T")) die_nomem(); if (!stralloc_cats(&rcptto,addr.s)) die_nomem(); if (!stralloc_0(&rcptto)) die_nomem(); out("250 ok\r\n");}
//saferead,从网络读len个字节到buf缓冲区
//返回实际读到的字节数.
//超时值为control/timeoutsmtpd文件中指定的值.见setup()函数,(默认值1200秒)int saferead(fd,buf,len) int fd; char *buf; int len;{ int r; flush(); r = timeoutread(timeout,fd,buf,len); if (r == -1) if (errno == error_timeout) die_alarm(); if (r <= 0) die_read(); return r;}char ssinbuf[1024];substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);struct qmail qqt;unsigned int bytestooverflow = 0;void put(ch)char *ch;{ if (bytestooverflow) if (!--bytestooverflow) qmail_fail(&qqt); qmail_put(&qqt,ch,1);}void blast(hops)int *hops;{ char ch; int state; int flaginheader; int pos; /* number of bytes since most recent \n, if fih */ int flagmaybex; /* 1 if this line might match RECEIVED, if fih */ int flagmaybey; /* 1 if this line might match \r\n, if fih */ int flagmaybez; /* 1 if this line might match DELIVERED, if fih */ state = 1; *hops = 0; flaginheader = 1; pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; for (;;) {
//从标准输入(也就是网络)读邮件内容直到独到仅有一个点的行. substdio_get(&ssin,&ch,1); if (flaginheader) { if (pos < 9) { if (ch != "delivered"[pos]) if (ch != "DELIVERED"[pos]) flagmaybez = 0; if (flagmaybez) if (pos == 8) ++*hops; if (pos < 8) if (ch != "received"[pos]) if (ch != "RECEIVED"[pos]) flagmaybex = 0; if (flagmaybex) if (pos == 7) ++*hops; if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0; if (flagmaybey) if (pos == 1) flaginheader = 0; } ++pos; if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; } } switch(state) { case 0: if (ch == '\n') straynewline(); if (ch == '\r') { state = 4; continue; } break; case 1: /* \r\n */ if (ch == '\n') straynewline(); if (ch == '.') { state = 2; continue; } if (ch == '\r') { state = 4; continue; } state = 0; break; case 2: /* \r\n + . */ if (ch == '\n') straynewline(); if (ch == '\r') { state = 3; continue; } state = 0; break; case 3: /* \r\n + .\r */ if (ch == '\n') return; put("."); put("\r"); if (ch == '\r') { state = 4; continue; } state = 0; break; case 4: /* + \r */ if (ch == '\n') { state = 1; break; } if (ch != '\r') { put("\r"); state = 0; } } put(&ch); }}char accept_buf[FMT_ULONG];void acceptmessage(qp) unsigned long qp;{ datetime_sec when; when = now(); out("250 ok "); accept_buf[fmt_ulong(accept_buf,(unsigned long) when)] = 0; out(accept_buf); out(" qp "); accept_buf[fmt_ulong(accept_buf,qp)] = 0; out(accept_buf); out("\r\n");}
//data命令解释程序
//完成向qmail-queue投递邮件void smtp_data() { int hops; unsigned long qp; char *qqx; if (!seenmail) { err_wantmail(); return; } //如果没有执行过mail命令,出错返回 if (!rcptto.len) { err_wantrcpt(); return; } //如果没有执行rcpt命令,出错返回 seenmail = 0; //将mail命令标志失效
//databytes邮件最大长度,如果没有指定那么值是0,代表无限 if (databytes) bytestooverflow = databytes + 1; if (qmail_open(&qqt) == -1) { err_qqt(); return; } //建立子进程执行qmail-queue qp = qmail_qp(&qqt); //qp为qmail-queue process缩写,it's a process id。 out("354 go ahead\r\n");
//向新建立的进程传送邮件头 received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo); blast(&hops); hops = (hops >= MAXHOPS); if (hops) qmail_fail(&qqt);
//向qmail-queue传送邮件头信息,如果hong@hg.org向lyx@hg.org发送邮件,那么向qmail-queue
//传送的字符串将是 Fhong@hg.orgTlyx@hg.org qmail_from(&qqt,mailfrom.s); qmail_put(&qqt,rcptto.s,rcptto.len); qqx = qmail_close(&qqt); if (!*qqx) { acceptmessage(qp); return; } //如果接受成功 if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; } if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; } if (*qqx == 'D') out("554 "); else out("451 "); out(qqx + 1); out("\r\n");}
//smtp命令处理函数表struct commands smtpcommands[] = { { "rcpt", smtp_rcpt, 0 }, { "mail", smtp_mail, 0 }, { "data", smtp_data, flush } //建立子进程执行qmail-queue,并向其传送邮件., { "quit", smtp_quit, flush }, { "helo", smtp_helo, flush }, { "ehlo", smtp_ehlo, flush }, { "rset", smtp_rset, 0 }, { "help", smtp_help, flush }, { "noop", err_noop, flush } //实际上未实现的命令, { "vrfy", err_vrfy, flush } //实际上未实现的命令, { 0, err_unimpl, flush } //命令错误} ;
/*
qmail-smtpd 是由tcp-env程序启动
tcp-env将来自网络的连接重定向到qmail-smtpd的标准输入及标准输出.
这些程式建立一些环境变量(如TCPREMOTEHOST,TCPREMOTEIP)将由setup()函数使用
*/
void main(){ sig_pipeignore(); //忽略信号 if (chdir(auto_qmail) == -1) die_control(); //改变当前目录到/var/qmail setup(); //读控制文件及相应的环境变量 if (ipme_init() != 1) die_ipme(); //取本地接口的ip地址 smtp_greet("220 "); //显示欢迎信息 out(" ESMTP\r\n");
//从标准读入(网络连接)读入smtp命令 if (commands(&ssin,&smtpcommands) == 0) die_read(); die_nomem();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -