<?xml version="1.0" standalone="yes"?>
<?xml-stylesheet type="text/xsl" href="css/rss.xslt"?>
<?xml-stylesheet type="text/css" href="css/rss.css"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>pencat's blog - Anti Spam</title><link>http://blog.bigcomic.com/</link><description>我的日记 - </description><generator>RainbowSoft Studio Z-Blog 1.8 Devo Build 80201</generator><language>zh-CN</language><copyright>Copyright © 1998-2007 bigcomic.com All rights reserved.</copyright><pubDate>Tue, 07 Sep 2010 18:08:07 +0800</pubDate><item><title>检查IP反向解析记录</title><author>pencat@bigfoot.com (pencat)</author><link>http://blog.bigcomic.com/post/203.html</link><pubDate>Thu, 22 Nov 2007 19:45:13 +0800</pubDate><guid>http://blog.bigcomic.com/post/203.html</guid><description><![CDATA[http://postmaster.info.aol.com/tools/rdns.html]]></description><category>Anti Spam</category><comments>http://blog.bigcomic.com/post/203.html#comment</comments><wfw:comment>http://blog.bigcomic.com/</wfw:comment><wfw:commentRss>http://blog.bigcomic.com/feed.asp?cmt=203</wfw:commentRss><trackback:ping>http://blog.bigcomic.com/cmd.asp?act=tb&amp;id=203&amp;key=8f098108</trackback:ping></item><item><title>构思随笔</title><author>pencat@bigfoot.com (pencat)</author><link>http://blog.bigcomic.com/post/202.html</link><pubDate>Thu, 22 Nov 2007 08:42:22 +0800</pubDate><guid>http://blog.bigcomic.com/post/202.html</guid><description><![CDATA[<a target="_blank" href="http://blog.bigcomic.com/upload/Note_13_191027.JPG"><img border="0" alt="" src="http://blog.bigcomic.com/upload/Note_13_191027.JPG" /></a>]]></description><category>Anti Spam</category><comments>http://blog.bigcomic.com/post/202.html#comment</comments><wfw:comment>http://blog.bigcomic.com/</wfw:comment><wfw:commentRss>http://blog.bigcomic.com/feed.asp?cmt=202</wfw:commentRss><trackback:ping>http://blog.bigcomic.com/cmd.asp?act=tb&amp;id=202&amp;key=b6b120eb</trackback:ping></item><item><title>Spam Locker for win</title><author>pencat@bigfoot.com (pencat)</author><link>http://blog.bigcomic.com/post/199.html</link><pubDate>Thu, 01 Nov 2007 11:15:42 +0800</pubDate><guid>http://blog.bigcomic.com/post/199.html</guid><description><![CDATA[<p><a href="http://blog.bigcomic.com/119388685525.png" rel="lightbox" title="119388685525.png"><img src="http://blog.bigcomic.com/upload/119388685525_p0.png" style="WIDTH: 500px; HEIGHT: 394px" title="119388685525_p0.png" height="394" width="500" alt="119388685525.png" border="0" id="urn:zoundry:jid:119388685525.png"/></a></p><p><br/><br/><a href="http://blog.bigcomic.com/upload/p1.png" rel="lightbox" title="p1.png"><img src="http://blog.bigcomic.com/upload/p1.png" style="WIDTH: 500px; HEIGHT: 265px" title="p1.png" height="265" width="500" alt="p1.png" border="0" id="urn:zoundry:jid:p1.png"/></a></p><br/><p><a href="http://blog.bigcomic.com/upload/p2.png" rel="lightbox" title="p2.png"><img src="http://blog.bigcomic.com/upload/p2.png" style="WIDTH: 500px; HEIGHT: 282px" title="p2.png" height="282" width="500" alt="p2.png" border="0" id="urn:zoundry:jid:p2.png"/></a></p>]]></description><category>Anti Spam</category><comments>http://blog.bigcomic.com/post/199.html#comment</comments><wfw:comment>http://blog.bigcomic.com/</wfw:comment><wfw:commentRss>http://blog.bigcomic.com/feed.asp?cmt=199</wfw:commentRss><trackback:ping>http://blog.bigcomic.com/cmd.asp?act=tb&amp;id=199&amp;key=90b9506a</trackback:ping></item><item><title>QMail 1.03 APF2.0补丁的修改</title><author>pencat@bigfoot.com (pencat)</author><link>http://blog.bigcomic.com/post/194.html</link><pubDate>Wed, 31 Oct 2007 13:38:47 +0800</pubDate><guid>http://blog.bigcomic.com/post/194.html</guid><description><![CDATA[apf对认证过的用户是不进行垃圾判断的, 虽然逻辑上没问题,但这样就不能对本地管理域做信任关系的控制.<br/><br/>比如: QMAIL本地域为abc.com  team@abc.com 的信任关系是,除了user1@abc.com之外,拒绝一切来信. 但apf对smtp认证过的用户视而不见, user2@abc.com 经过认证也可以发信到team@abc.com 这样就存在一个小BUG. 这种情况下只有修改qmail-smtpd.c 重新编译.<br/><br/> //NO AUTH 后面的代码注释掉即可 <br/><br/><textarea name="code" language="c#">#include "sig.h"<br/>#include "readwrite.h"<br/>#include "stralloc.h"<br/>#include "substdio.h"<br/>#include "alloc.h"<br/>#include "auto_qmail.h"<br/>#include "control.h"<br/>#include "received.h"<br/>#include "constmap.h"<br/>#include "error.h"<br/>#include "ipme.h"<br/>#include "ip.h"<br/>#include "qmail.h"<br/>#include "str.h"<br/>#include "fmt.h"<br/>#include "scan.h"<br/>#include "byte.h"<br/>#include "case.h"<br/>#include "env.h"<br/>#include "now.h"<br/>#include "exit.h"<br/>#include "rcpthosts.h"<br/>#include "timeoutread.h"<br/>#include "timeoutwrite.h"<br/>#include "commands.h"<br/>#include "wait.h"<br/>#include "fd.h"<br/><br/>#define AUTHCRAM<br/>#define MAXHOPS 100<br/>unsigned int databytes = 0;<br/>int timeout = 1200;<br/><br/>/* APF patch for qmail, experimental version, but more stable<br/> * than before, use it as your own risk!<br/> *<br/> * Patch arch designer : nsinit <nsinit@263.net><br/> *              Author : nsinit <nsinit@263.net><br/> *       Patch updater : He zhiqiang <hzqbbc@damail.cn><br/> *<br/> * TODO: complete the protocol implemention under qmail and<br/> *       add some new feature depends on qmail arch.<br/> */<br/><br/>void out(char *);<br/>void die_nomem(void);<br/><br/>/*<br/> * APF:<br/> * apf_server's arguments: <br/> * 1. protocol_state <br/> * 2. protocol_name <br/> * 3. helo_name <br/> * 4. queue_id             // XXX -- cann't get before DATA command<br/> * 5. sender<br/> * 6. recipient <br/> * 7. client_address<br/> * 8. client_name          // XXX -- gethostbyaddr .. TODO<br/> */<br/><br/>#define        STATE_HELO      0<br/>#define        STATE_MAIL      1<br/>#define        STATE_RCPT      2<br/><br/>/* APF ret code defined here */<br/>#define		APF_FAIL	255	/* APF-qmail exec fail */<br/>#define		APF_UNEXP	254	/* APF-qmail return unknow */<br/>#define		APF_ACPT	0	/* APF-qmail return ok/dunno */<br/>#define		APF_ERR		1	/* APF-qmail exec success */<br/><br/>typedef struct apf_request {<br/>	stralloc        protocol_state;<br/>	stralloc        protocol_name;<br/>	stralloc        helo_name;<br/>	stralloc        queue_id;<br/>	stralloc        sender;<br/>	stralloc        recipient;<br/>	stralloc        client_address;<br/>	stralloc        client_name;<br/>} apf_request_t;<br/><br/>apf_request_t apf_request = {0};<br/>stralloc apf_server = {0};     // initialized reading from control/xxx<br/><br/>void    apf_debug()<br/>{<br/>/* <br/> * so stupid, should write my own wrapper for substdio ssout:<br/> * OUT(char* format, char* str)<br/> */<br/>	out("250 ok DEBUG->");<br/>	out("apf_server: ");<br/>	out(apf_server.s);<br/>	out("; ");<br/>	out("remoteip: ");<br/>	out(apf_request.client_address.s);<br/>	out("; ");<br/>	out("helo: ");<br/>	out(apf_request.helo_name.s ? apf_request.helo_name.s : "");<br/>	out("; ");<br/>	out("sender: ");<br/>	out(apf_request.sender.s ? apf_request.sender.s : "");<br/>	out("; ");<br/>	out("recipient: ");<br/>	out(apf_request.recipient.s ? apf_request.recipient.s : "");<br/>	out("; ");<br/>}<br/><br/>/*<br/> * do_apf_rcpt routine<br/> */<br/>int     do_apf_rcpt(apf_request_t* apf)<br/>{<br/>	int     child, wstat, ret = -1;<br/>	char* (args[8]);<br/><br/>	if(!stralloc_copys(&(apf->protocol_state), "RCPT")) die_nomem();<br/>	if(!stralloc_0(&(apf->protocol_state))) die_nomem();<br/>	if(!stralloc_copys(&(apf->protocol_name), "SMTP")) die_nomem();<br/>	if(!stralloc_0(&(apf->protocol_name))) die_nomem();<br/>	args[0] = apf_server.s;<br/>	args[1] = apf->protocol_state.s;<br/>	args[2] = apf->protocol_name.s;<br/>	args[3] = apf->helo_name.s ? apf->helo_name.s : "";<br/>	args[4] = apf->sender.s ? apf->sender.s : "";<br/>	args[5] = apf->recipient.s ? apf->recipient.s : "";<br/>	args[6] = apf->client_address.s;<br/>	args[7] = 0;<br/><br/>	//apf_debug();<br/><br/>	switch(child = fork())<br/>	{<br/>		case -1:<br/>			return ret;<br/>		case 0:<br/>			sig_pipedefault();<br/>			execv(*args,args);<br/>			_exit(APF_FAIL);<br/>	}<br/>	wait_pid(&wstat,child);<br/>	if (wait_crashed(wstat))<br/>		return ret;<br/>	return wait_exitcode(wstat);<br/>}<br/><br/>/*<br/> * general entry for various real do_apf_XXX routines<br/> */<br/>int     do_apf (apf_request_t* apf, int state)<br/>{<br/>	int     ret = -1;<br/>	switch (state) {<br/>		case STATE_HELO: // not implement<br/>			break;<br/>		case STATE_MAIL: // not implement<br/>			break;<br/>		case STATE_RCPT:<br/>			ret = do_apf_rcpt(apf);<br/>			break;<br/>		default:<br/>			out("Error APF state: ");<br/>			out(apf->protocol_state.s);<br/>			out("\r\n");<br/>			break;<br/>	}<br/><br/>	return ret;<br/>}<br/><br/>int safewrite(fd,buf,len) int fd; char *buf; int len;<br/>{<br/>  int r;<br/>  r = timeoutwrite(timeout,fd,buf,len);<br/>  if (r <= 0) _exit(1);<br/>  return r;<br/>}<br/><br/>char ssoutbuf[512];<br/>substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);<br/><br/>void flush() { substdio_flush(&ssout); }<br/>void out(s) char *s; { substdio_puts(&ssout,s); }<br/><br/>void die_read() { _exit(1); }<br/>void die_alarm() { out("451 timeout (#4.4.2)\r\n"); flush(); _exit(1); }<br/>void die_nomem() { out("421 out of memory (#4.3.0)\r\n"); flush(); _exit(1); }<br/>void die_control() { out("421 unable to read controls (#4.3.0)\r\n"); flush(); _exit(1); }<br/>void die_ipme() { out("421 unable to figure out my IP addresses (#4.3.0)\r\n"); flush(); _exit(1); }<br/>void straynewline() { out("451 See http://pobox.com/~djb/docs/smtplf.html.\r\n"); flush(); _exit(1); }<br/><br/>void err_bmf() { out("553 sorry, your envelope sender is in my badmailfrom list (#5.7.1)\r\n"); }<br/>void err_nogateway() { out("553 sorry, that domain isn't in my list of allowed rcpthosts (#5.7.1)\r\n"); }<br/>void err_unimpl() { out("502 unimplemented (#5.5.1)\r\n"); }<br/>void err_syntax() { out("555 syntax error (#5.5.4)\r\n"); }<br/>void err_wantmail() { out("503 MAIL first (#5.5.1)\r\n"); }<br/>void err_wantrcpt() { out("503 RCPT first (#5.5.1)\r\n"); }<br/>void err_noop() { out("250 ok\r\n"); }<br/>void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); }<br/>void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); }<br/><br/>int err_child() { out("454 oops, problem with child and I can't auth (#4.3.0)\r\n"); return -1; }<br/>int err_fork() { out("454 oops, child won't start and I can't auth (#4.3.0)\r\n"); return -1; }<br/>int err_pipe() { out("454 oops, unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; }<br/>int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; }<br/>void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); }<br/>void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); }<br/>int err_noauth() { out("504 auth type unimplemented (#5.5.1)\r\n"); return -1; }<br/>int err_authabrt() { out("501 auth exchange cancelled (#5.0.0)\r\n"); return -1; }<br/>int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; }<br/><br/>stralloc greeting = {0};<br/><br/>void smtp_greet(code) char *code;<br/>{<br/>  substdio_puts(&ssout,code);<br/>  substdio_put(&ssout,greeting.s,greeting.len);<br/>}<br/>void smtp_help()<br/>{<br/>  out("214 qmail home page: http://pobox.com/~djb/qmail.html\r\n");<br/>}<br/>void smtp_quit()<br/>{<br/>  smtp_greet("221 "); out("\r\n"); flush(); _exit(0);<br/>}<br/><br/>char *remoteip;<br/>char *remotehost;<br/>char *remoteinfo;<br/>char *local;<br/>char *relayclient;<br/><br/>stralloc helohost = {0};<br/>char *fakehelo; /* pointer into helohost, or 0 */<br/><br/>void dohelo(arg) char *arg; {<br/>  if (!stralloc_copys(&helohost,arg)) die_nomem(); <br/>  if (!stralloc_0(&helohost)) die_nomem();<br/>  fakehelo = case_diffs(remotehost,helohost.s) ? helohost.s : 0;<br/>}<br/><br/>int liphostok = 0;<br/>stralloc liphost = {0};<br/>int bmfok = 0;<br/>stralloc bmf = {0};<br/>struct constmap mapbmf;<br/><br/>void setup()<br/>{<br/>  char *x;<br/>  unsigned long u;<br/> <br/>  if (control_init() == -1) die_control();<br/>  if (control_rldef(&greeting,"control/smtpgreeting",1,(char *) 0) != 1)<br/>    die_control();<br/>  /* get apf_server control file */<br/>  if (control_rldef(&apf_server,"control/apf_server",1,(char *) 0) != 1)<br/>    die_control();<br/>  stralloc_append(&apf_server, "");    // remove the trailing '\n' && 'Z'<br/><br/>  liphostok = control_rldef(&liphost,"control/localiphost",1,(char *) 0);<br/>  if (liphostok == -1) die_control();<br/>  if (control_readint(&timeout,"control/timeoutsmtpd") == -1) die_control();<br/>  if (timeout <= 0) timeout = 1;<br/><br/>  if (rcpthosts_init() == -1) die_control();<br/><br/>  bmfok = control_readfile(&bmf,"control/badmailfrom",0);<br/>  if (bmfok == -1) die_control();<br/>  if (bmfok)<br/>    if (!constmap_init(&mapbmf,bmf.s,bmf.len,0)) die_nomem();<br/> <br/>  if (control_readint(&databytes,"control/databytes") == -1) die_control();<br/>  x = env_get("DATABYTES");<br/>  if (x) { scan_ulong(x,&u); databytes = u; }<br/>  if (!(databytes + 1)) --databytes;<br/> <br/>  remoteip = env_get("TCPREMOTEIP");<br/>  if (!remoteip) remoteip = "unknown";<br/>  /* get client_address for APF */<br/>  if(!stralloc_copys(&(apf_request.client_address), remoteip)) die_nomem();<br/>  if(!stralloc_0(&apf_request.client_address)) die_nomem();<br/><br/>  local = env_get("TCPLOCALHOST");<br/>  if (!local) local = env_get("TCPLOCALIP");<br/>  if (!local) local = "unknown";<br/>  remotehost = env_get("TCPREMOTEHOST");<br/>  if (!remotehost) remotehost = "unknown";<br/>  remoteinfo = env_get("TCPREMOTEINFO");<br/>  relayclient = env_get("RELAYCLIENT");<br/>  dohelo(remotehost);<br/>}<br/><br/><br/>stralloc addr = {0}; /* will be 0-terminated, if addrparse returns 1 */<br/><br/>int addrparse(arg)<br/>char *arg;<br/>{<br/>  int i;<br/>  char ch;<br/>  char terminator;<br/>  struct ip_address ip;<br/>  int flagesc;<br/>  int flagquoted;<br/> <br/>  terminator = '>';<br/>  i = str_chr(arg,'<');<br/>  if (arg[i])<br/>    arg += i + 1;<br/>  else { /* partner should go read rfc 821 */<br/>    terminator = ' ';<br/>    arg += str_chr(arg,':');<br/>    if (*arg == ':') ++arg;<br/>    while (*arg == ' ') ++arg;<br/>  }<br/><br/>  /* strip source route */<br/>  if (*arg == '@') while (*arg) if (*arg++ == ':') break;<br/><br/>  if (!stralloc_copys(&addr,"")) die_nomem();<br/>  flagesc = 0;<br/>  flagquoted = 0;<br/>  for (i = 0;ch = arg[i];++i) { /* copy arg to addr, stripping quotes */<br/>    if (flagesc) {<br/>      if (!stralloc_append(&addr,&ch)) die_nomem();<br/>      flagesc = 0;<br/>    }<br/>    else {<br/>      if (!flagquoted && (ch == terminator)) break;<br/>      switch(ch) {<br/>        case '\\': flagesc = 1; break;<br/>        case '"': flagquoted = !flagquoted; break;<br/>        default: if (!stralloc_append(&addr,&ch)) die_nomem();<br/>      }<br/>    }<br/>  }<br/>  /* could check for termination failure here, but why bother? */<br/>  if (!stralloc_append(&addr,"")) die_nomem();<br/><br/>  if (liphostok) {<br/>    i = byte_rchr(addr.s,addr.len,'@');<br/>    if (i < addr.len) /* if not, partner should go read rfc 821 */<br/>      if (addr.s[i + 1] == '[')<br/>        if (!addr.s[i + 1 + ip_scanbracket(addr.s + i + 1,&ip)])<br/>          if (ipme_is(&ip)) {<br/>            addr.len = i + 1;<br/>            if (!stralloc_cat(&addr,&liphost)) die_nomem();<br/>            if (!stralloc_0(&addr)) die_nomem();<br/>          }<br/>  }<br/><br/>  if (addr.len > 900) return 0;<br/>  return 1;<br/>}<br/><br/>int bmfcheck()<br/>{<br/>  int j;<br/>  if (!bmfok) return 0;<br/>  if (constmap(&mapbmf,addr.s,addr.len - 1)) return 1;<br/>  j = byte_rchr(addr.s,addr.len,'@');<br/>  if (j < addr.len)<br/>    if (constmap(&mapbmf,addr.s + j,addr.len - j - 1)) return 1;<br/>  return 0;<br/>}<br/><br/>int addrallowed()<br/>{<br/>  int r;<br/>  r = rcpthosts(addr.s,str_len(addr.s));<br/>  if (r == -1) die_control();<br/>  return r;<br/>}<br/><br/><br/>int seenmail = 0;<br/>int flagbarf; /* defined if seenmail */<br/>stralloc mailfrom = {0};<br/>stralloc rcptto = {0};<br/><br/>void smtp_helo(arg) char *arg;<br/>{<br/>/* get helo_name for APF */<br/>  if (!stralloc_copys(&(apf_request.helo_name),arg)) die_nomem();<br/>  if (!stralloc_0(&(apf_request.helo_name))) die_nomem();<br/><br/>  smtp_greet("250 "); out("\r\n");<br/>  seenmail = 0; dohelo(arg);<br/>}<br/>void smtp_ehlo(arg) char *arg;<br/>{<br/>/* get helo_name, older version does not<br/> * handle ehlo command */<br/>  if (!stralloc_copys(&(apf_request.helo_name),arg)) die_nomem();<br/>  if (!stralloc_0(&(apf_request.helo_name))) die_nomem();<br/><br/>  smtp_greet("250-");<br/>#ifdef AUTHCRAM<br/>  out("\r\n250-AUTH LOGIN CRAM-MD5 PLAIN");<br/>  out("\r\n250-AUTH=LOGIN CRAM-MD5 PLAIN");<br/>#else<br/>  out("\r\n250-AUTH LOGIN PLAIN");<br/>  out("\r\n250-AUTH=LOGIN PLAIN");<br/>#endif<br/>  out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");<br/>  seenmail = 0; dohelo(arg);<br/>}<br/>void smtp_rset()<br/>{<br/>  seenmail = 0;<br/>  out("250 flushed\r\n");<br/>}<br/>void smtp_mail(arg) char *arg;<br/>{<br/>  if (!addrparse(arg)) { err_syntax(); return; }<br/>  flagbarf = bmfcheck();<br/>  seenmail = 1;<br/>  if (!stralloc_copys(&rcptto,"")) die_nomem();<br/>  if (!stralloc_copys(&mailfrom,addr.s)) die_nomem();<br/>  if (!stralloc_0(&mailfrom)) die_nomem();<br/>  /* get sender for APF */<br/>  if (!stralloc_copys(&(apf_request.sender),addr.s)) die_nomem();<br/>  if (!stralloc_0(&(apf_request.sender))) die_nomem();<br/><br/>  out("250 ok\r\n");<br/>}<br/><br/>/* To work with SMTP AUTH authenticated user under APF<br/> * which will not step into do_apf() function and omit the<br/> * checking, use int authd to judge :-) the easy way.*/<br/>extern int authd;<br/>void smtp_rcpt(arg) char *arg; {<br/>  int ret= 0;<br/>  if (!seenmail) { err_wantmail(); return; }<br/>  if (!addrparse(arg)) { err_syntax(); return; }<br/>  if (flagbarf) { err_bmf(); return; }<br/>  if (relayclient) {<br/>    --addr.len;<br/>    if (!stralloc_cats(&addr,relayclient)) die_nomem();<br/>    if (!stralloc_0(&addr)) die_nomem();<br/>  }<br/>  else<br/>    if (!addrallowed()) { err_nogateway(); return; }<br/>  if (!stralloc_cats(&rcptto,"T")) die_nomem();<br/>  if (!stralloc_cats(&rcptto,addr.s)) die_nomem();<br/>  if (!stralloc_0(&rcptto)) die_nomem();<br/>  /* get recipient for APF */<br/>  if (!stralloc_copys(&(apf_request.recipient),addr.s)) die_nomem();<br/>  if (!stralloc_0(&(apf_request.recipient))) die_nomem();<br/><br/>  /* Ok it's the right time to do apf checking if not<br/>   * authenticated via SMTP AUTH modules */<br/><br/>  //NO AUTH if ( !authd ) {<br/>    ret = do_apf(&apf_request, STATE_RCPT);<br/>    switch ( ret )<br/>    {<br/>	  /* on APF_FAIL & APF_UNEXP we still return 250 for non stop<br/>	   * service for production system, it will only receive more<br/>	   * spam temporarily instead of refusing service */<br/>	  case APF_FAIL:<br/>		  out("250 ok, but apf_server exec error\r\n");<br/>		  break;<br/>	  case APF_UNEXP:<br/>		  out("250 ok, but apf_server return unknow error\r\n");<br/>		  break;<br/>	  case APF_ERR:<br/>		  /* here we does not need to output any msg, apf-qmail<br/>		   * will do it for us */<br/>		  seenmail=0;<br/>		  break;<br/>	  case APF_ACPT:<br/>		  out("250 policy checking passed\r\n");<br/>		  seenmail=1;<br/>		  break;<br/>	  default:<br/>		  out("250 ok\r\n");<br/>		  seenmail=1;<br/>		  break;<br/>    }<br/>  //NO AUTH } else {<br/>  //NO AUTH 	/* let them go, we will not check authenticated user */<br/>  //NO AUTH 	out("250 ok\r\n");<br/>  //NO AUTH 	seenmail=1;<br/>  //NO AUTH   }<br/>}<br/><br/><br/>int saferead(fd,buf,len) int fd; char *buf; int len;<br/>{<br/>  int r;<br/>  flush();<br/>  r = timeoutread(timeout,fd,buf,len);<br/>  if (r == -1) if (errno == error_timeout) die_alarm();<br/>  if (r <= 0) die_read();<br/>  return r;<br/>}<br/><br/>char ssinbuf[1024];<br/>substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);<br/><br/>struct qmail qqt;<br/>unsigned int bytestooverflow = 0;<br/><br/>void put(ch)<br/>char *ch;<br/>{<br/>  if (bytestooverflow)<br/>    if (!--bytestooverflow)<br/>      qmail_fail(&qqt);<br/>  qmail_put(&qqt,ch,1);<br/>}<br/><br/>void blast(hops)<br/>int *hops;<br/>{<br/>  char ch;<br/>  int state;<br/>  int flaginheader;<br/>  int pos; /* number of bytes since most recent \n, if fih */<br/>  int flagmaybex; /* 1 if this line might match RECEIVED, if fih */<br/>  int flagmaybey; /* 1 if this line might match \r\n, if fih */<br/>  int flagmaybez; /* 1 if this line might match DELIVERED, if fih */<br/> <br/>  state = 1;<br/>  *hops = 0;<br/>  flaginheader = 1;<br/>  pos = 0; flagmaybex = flagmaybey = flagmaybez = 1;<br/>  for (;;) {<br/>    substdio_get(&ssin,&ch,1);<br/>    if (flaginheader) {<br/>      if (pos < 9) {<br/>        if (ch != "delivered"[pos]) if (ch != "DELIVERED"[pos]) flagmaybez = 0;<br/>        if (flagmaybez) if (pos == 8) ++*hops;<br/>        if (pos < 8)<br/>          if (ch != "received"[pos]) if (ch != "RECEIVED"[pos]) flagmaybex = 0;<br/>        if (flagmaybex) if (pos == 7) ++*hops;<br/>        if (pos < 2) if (ch != "\r\n"[pos]) flagmaybey = 0;<br/>        if (flagmaybey) if (pos == 1) flaginheader = 0;<br/>      }<br/>      ++pos;<br/>      if (ch == '\n') { pos = 0; flagmaybex = flagmaybey = flagmaybez = 1; }<br/>    }<br/>    switch(state) {<br/>      case 0:<br/>        if (ch == '\n') straynewline();<br/>        if (ch == '\r') { state = 4; continue; }<br/>        break;<br/>      case 1: /* \r\n */<br/>        if (ch == '\n') straynewline();<br/>        if (ch == '.') { state = 2; continue; }<br/>        if (ch == '\r') { state = 4; continue; }<br/>        state = 0;<br/>        break;<br/>      case 2: /* \r\n + . */<br/>        if (ch == '\n') straynewline();<br/>        if (ch == '\r') { state = 3; continue; }<br/>        state = 0;<br/>        break;<br/>      case 3: /* \r\n + .\r */<br/>        if (ch == '\n') return;<br/>        put(".");<br/>        put("\r");<br/>        if (ch == '\r') { state = 4; continue; }<br/>        state = 0;<br/>        break;<br/>      case 4: /* + \r */<br/>        if (ch == '\n') { state = 1; break; }<br/>        if (ch != '\r') { put("\r"); state = 0; }<br/>    }<br/>    put(&ch);<br/>  }<br/>}<br/><br/>char accept_buf[FMT_ULONG];<br/>void acceptmessage(qp) unsigned long qp;<br/>{<br/>  datetime_sec when;<br/>  when = now();<br/>  out("250 ok ");<br/>  accept_buf[fmt_ulong(accept_buf,(unsigned long) when)] = 0;<br/>  out(accept_buf);<br/>  out(" qp ");<br/>  accept_buf[fmt_ulong(accept_buf,qp)] = 0;<br/>  out(accept_buf);<br/>  out("\r\n");<br/>}<br/><br/>void smtp_data() {<br/>  int hops;<br/>  unsigned long qp;<br/>  char *qqx;<br/> <br/>  if (!seenmail) { err_wantmail(); return; }<br/>  if (!rcptto.len) { err_wantrcpt(); return; }<br/>  seenmail = 0;<br/>  if (databytes) bytestooverflow = databytes + 1;<br/>  if (qmail_open(&qqt) == -1) { err_qqt(); return; }<br/>  qp = qmail_qp(&qqt);<br/>  out("354 go ahead\r\n");<br/> <br/>  received(&qqt,"SMTP",local,remoteip,remotehost,remoteinfo,fakehelo);<br/>  blast(&hops);<br/>  hops = (hops >= MAXHOPS);<br/>  if (hops) qmail_fail(&qqt);<br/>  qmail_from(&qqt,mailfrom.s);<br/>  qmail_put(&qqt,rcptto.s,rcptto.len);<br/> <br/>  qqx = qmail_close(&qqt);<br/>  if (!*qqx) { acceptmessage(qp); return; }<br/>  if (hops) { out("554 too many hops, this message is looping (#5.4.6)\r\n"); return; }<br/>  if (databytes) if (!bytestooverflow) { out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n"); return; }<br/>  if (*qqx == 'D') out("554 "); else out("451 ");<br/>  out(qqx + 1);<br/>  out("\r\n");<br/>}<br/><br/><br/>char unique[FMT_ULONG + FMT_ULONG + 3];<br/>static stralloc authin = {0};<br/>static stralloc user = {0};<br/>static stralloc pass = {0};<br/>static stralloc resp = {0};<br/>static stralloc slop = {0};<br/>char *hostname;<br/>char **childargs;<br/>substdio ssup;<br/>char upbuf[128];<br/>int authd = 0;<br/><br/>int authgetl(void) {<br/>  int i;<br/><br/>  if (!stralloc_copys(&authin, "")) die_nomem();<br/><br/>  for (;;) {<br/>    if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */<br/>    i = substdio_get(&ssin,authin.s + authin.len,1);<br/>    if (i != 1) die_read();<br/>    if (authin.s[authin.len] == '\n') break;<br/>    ++authin.len;<br/>  }<br/><br/>  if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len;<br/>  authin.s[authin.len] = 0;<br/><br/>  if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); }<br/>  if (authin.len == 0) { return err_input(); }<br/>  return authin.len;<br/>}<br/><br/>int authenticate(void)<br/>{<br/>  int child;<br/>  int wstat;<br/>  int pi[2];<br/><br/>  if (!stralloc_0(&user)) die_nomem();<br/>  if (!stralloc_0(&pass)) die_nomem();<br/>  if (!stralloc_0(&resp)) die_nomem();<br/><br/>  if (fd_copy(2,1) == -1) return err_pipe();<br/>  close(3);<br/>  if (pipe(pi) == -1) return err_pipe();<br/>  if (pi[0] != 3) return err_pipe();<br/>  switch(child = fork()) {<br/>    case -1:<br/>      return err_fork();<br/>    case 0:<br/>      close(pi[1]);<br/>      sig_pipedefault();<br/>      execvp(*childargs, childargs);<br/>      _exit(1);<br/>  }<br/>  close(pi[0]);<br/><br/>  substdio_fdbuf(&ssup,write,pi[1],upbuf,sizeof upbuf);<br/>  if (substdio_put(&ssup,user.s,user.len) == -1) return err_write();<br/>  if (substdio_put(&ssup,pass.s,pass.len) == -1) return err_write();<br/>  if (substdio_put(&ssup,resp.s,resp.len) == -1) return err_write();<br/>  if (substdio_flush(&ssup) == -1) return err_write();<br/><br/>  close(pi[1]);<br/>  byte_zero(pass.s,pass.len);<br/>  byte_zero(upbuf,sizeof upbuf);<br/>  if (wait_pid(&wstat,child) == -1) return err_child();<br/>  if (wait_crashed(wstat)) return err_child();<br/>  if (wait_exitcode(wstat)) { sleep(5); return 1; } /* no */<br/>  return 0; /* yes */<br/>}<br/><br/>int auth_login(arg) char *arg;<br/>{<br/>  int r;<br/><br/>  if (*arg) {<br/>    if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input();<br/>  }<br/>  else {<br/>    out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */<br/>    if (authgetl() < 0) return -1;<br/>    if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input();<br/>  }<br/>  if (r == -1) die_nomem();<br/><br/>  out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */<br/><br/>  if (authgetl() < 0) return -1;<br/>  if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input();<br/>  if (r == -1) die_nomem();<br/><br/>  if (!user.len || !pass.len) return err_input();<br/>  return authenticate();  <br/>}<br/><br/>int auth_plain(arg) char *arg;<br/>{<br/>  int r, id = 0;<br/><br/>  if (*arg) {<br/>    if (r = b64decode(arg,str_len(arg),&slop) == 1) return err_input();<br/>  }<br/>  else {<br/>    out("334 ok, go on\r\n"); flush();<br/>    if (authgetl() < 0) return -1;<br/>    if (r = b64decode(authin.s,authin.len,&slop) == 1) return err_input();<br/>  }<br/>  if (r == -1 || !stralloc_0(&slop)) die_nomem();<br/>  while (slop.s[id]) id++; /* ignore authorize-id */<br/><br/>  if (slop.len > id + 1)<br/>    if (!stralloc_copys(&user,slop.s + id + 1)) die_nomem();<br/>  if (slop.len > id + user.len + 2)<br/>    if (!stralloc_copys(&pass,slop.s + id + user.len + 2)) die_nomem();<br/><br/>  if (!user.len || !pass.len) return err_input();<br/>  return authenticate();<br/>}<br/><br/>#ifdef AUTHCRAM<br/>int auth_cram()<br/>{<br/>  int i, r;<br/>  char *s;<br/><br/>  s = unique;<br/>  s += fmt_uint(s,getpid());<br/>  *s++ = '.';<br/>  s += fmt_ulong(s,(unsigned long) now());<br/>  *s++ = '@';<br/>  *s++ = 0;<br/><br/>  if (!stralloc_copys(&pass,"<")) die_nomem();<br/>  if (!stralloc_cats(&pass,unique)) die_nomem();<br/>  if (!stralloc_cats(&pass,hostname)) die_nomem();<br/>  if (!stralloc_cats(&pass,">")) die_nomem();<br/>  if (b64encode(&pass,&slop) < 0) die_nomem();<br/>  if (!stralloc_0(&slop)) die_nomem();<br/><br/>  out("334 ");<br/>  out(slop.s);<br/>  out("\r\n");<br/>  flush();<br/><br/>  if (authgetl() < 0) return -1;<br/>  if (r = b64decode(authin.s,authin.len,&slop) == 1) return err_input();<br/>  if (r == -1 || !stralloc_0(&slop)) die_nomem();<br/><br/>  i = str_chr(slop.s,' ');<br/>  s = slop.s + i;<br/>  while (*s == ' ') ++s;<br/>  slop.s[i] = 0;<br/>  if (!stralloc_copys(&user,slop.s)) die_nomem();<br/>  if (!stralloc_copys(&resp,s)) die_nomem();<br/><br/>  if (!user.len || !resp.len) return err_input();<br/>  return authenticate();<br/>}<br/>#endif<br/><br/>struct authcmd {<br/>  char *text;<br/>  int (*fun)();<br/>} authcmds[] = {<br/>  { "login", auth_login }<br/>, { "plain", auth_plain }<br/>#ifdef AUTHCRAM<br/>, { "cram-md5", auth_cram }<br/>#endif<br/>, { 0, err_noauth }<br/>};<br/><br/>void smtp_auth(arg)<br/>char *arg;<br/>{<br/>  int i;<br/>  char *cmd = arg;<br/><br/>  if (!hostname || !*childargs)<br/>  {<br/>    out("503 auth not available (#5.3.3)\r\n");<br/>    return;<br/>  }<br/>  if (authd) { err_authd(); return; }<br/>  if (seenmail) { err_authmail(); return; }<br/><br/>  if (!stralloc_copys(&user,"")) die_nomem();<br/>  if (!stralloc_copys(&pass,"")) die_nomem();<br/>  if (!stralloc_copys(&resp,"")) die_nomem();<br/><br/>  i = str_chr(cmd,' ');   <br/>  arg = cmd + i;<br/>  while (*arg == ' ') ++arg;<br/>  cmd[i] = 0;<br/><br/>  for (i = 0;authcmds[i].text;++i)<br/>    if (case_equals(authcmds[i].text,cmd)) break;<br/><br/>  switch (authcmds[i].fun(arg)) {<br/>    case 0:<br/>      authd = 1;<br/>      relayclient = "";<br/>      remoteinfo = user.s;<br/>      out("235 ok, go ahead (#2.0.0)\r\n");<br/>      break;<br/>    case 1:<br/>      out("535 authorization failed (#5.7.0)\r\n");<br/>  }<br/>}<br/><br/>struct commands smtpcommands[] = {<br/>  { "rcpt", smtp_rcpt, 0 }<br/>, { "mail", smtp_mail, 0 }<br/>, { "data", smtp_data, flush }<br/>, { "auth", smtp_auth, flush }<br/>, { "quit", smtp_quit, flush }<br/>, { "helo", smtp_helo, flush }<br/>, { "ehlo", smtp_ehlo, flush }<br/>, { "rset", smtp_rset, 0 }<br/>, { "help", smtp_help, flush }<br/>, { "noop", err_noop, flush }<br/>, { "vrfy", err_vrfy, flush }<br/>, { 0, err_unimpl, flush }<br/>} ;<br/><br/>void main(argc,argv)<br/>int argc;<br/>char **argv;<br/>{<br/>  hostname = argv[1];<br/>  childargs = argv + 2;<br/><br/>  sig_pipeignore();<br/>  if (chdir(auto_qmail) == -1) die_control();<br/>  setup();<br/>  if (ipme_init() != 1) die_ipme();<br/>  smtp_greet("220 APF-qmail: ");<br/>  out(" ESMTP\r\n");<br/>  if (commands(&ssin,&smtpcommands) == 0) die_read();<br/>  die_nomem();<br/>}<br/><br/></textarea>]]></description><category>Anti Spam</category><comments>http://blog.bigcomic.com/post/194.html#comment</comments><wfw:comment>http://blog.bigcomic.com/</wfw:comment><wfw:commentRss>http://blog.bigcomic.com/feed.asp?cmt=194</wfw:commentRss><trackback:ping>http://blog.bigcomic.com/cmd.asp?act=tb&amp;id=194&amp;key=f1244ae0</trackback:ping></item><item><title>进我在反垃圾的路上狂奔 </title><author>pencat@bigfoot.com (pencat)</author><link>http://blog.bigcomic.com/post/192.html</link><pubDate>Mon, 29 Oct 2007 21:51:56 +0800</pubDate><guid>http://blog.bigcomic.com/post/192.html</guid><description><![CDATA[<p>两周前, 受命为公司部署反垃圾邮件系统. 那天我的噩梦开始了.</p><p>对邮件服务器的了解不多,只是很久以前学习TCP/IP的时候看过MAIL工作的一些原理, 现在也差不多忘干净了.&nbsp; 还好有个地方叫互连网, 我可以从那里得到想要的一切.</p><p>查过资料才知道,反垃圾邮件系统从功能上分为两类。</p><p>1. 基于内容 以SpamAssasin为代表。<br />2. 基于行为 以Spam Locker为代表。</p><p>SpamAssasin 被认为是最好的反垃圾软件，SpamAssasin将其逻辑封装在一个设计精良的、抽象化的API中，因此它可被集成到电子邮件数据流中的任何地方。它号称9x%的识别率。高识别率的代价是以牺牲性能换来的，每一封邮件都要使用大量规则去对比，系统开销可想而知。如果运行在千万级用户的系统上.......难以想象</p><p>而Spam Locker是基于行为的反垃圾，在SENDER与MAIL SERVER进行命令交换时就可以识别垃圾邮件，从而断开TCP连接，极大的节省了系统资源和带宽。而APF的机制更可以将反垃圾系统和邮件系统分别置于不同的服务器上，进一步提高了系统的稳定性，降低了资源成本。<br />这两种系统由于机制不同，所以没有可比性，但我更倾基于行为的反垃圾邮件系统。</p><p>两周前下载并安装了ExtMail发布的Spam Locker（以下简称SL）开源反垃圾邮件程序。SL采用PERL语言开发，通过补丁形式支持QMAIL。在MAIL SERVER接收到RCPT TO后调用APF插件，向指定IP发送如下数据:</p><p>request=smtpd_access_policy<br />protocol_state=RCPT<br />protocol_name=SMTP<br />client_address=82.135.209.55<br />client_name=unknow<br />helo_name=82-135-209-55.ip.zebra.lt<br /><a href="mailto:sender=pulp@dcba.com">sender=pulp@dcba.com</a><br /><a href="mailto:recipient=liming@abcd.com">recipient=liming@abcd.com</a><br />queue_id=</p><p>SL收到数据后调用各种模块对数据进行分析，最后返回OK/DUNNO/REJECT三种状态。但是到最后我也没弄清楚模块之间的优先级到底是怎样，导致正常邮件不断的被退回。</p><p>悲剧开始了。</p><p>总公司的邮件设置很奇怪，发信使用了第三方服务，造成MX记录不匹配。有些SMTP SERVER的IP还被RBL列入黑名单。这些自然都被SL挡了回去，设置白名单也无济于事。</p><p>总公司的人急了。</p><p>南京公司内部有3台SMTP，用户没有进行统一的设置，而另外2台也没有SMTP认证，导致发信者的域名与发信IP不符，结果就被当做垃圾。在本地DNS增加了MX记录记录也不行。SL的日志中明明读取了本地DNS的信息，也返回了我设置的记录，依旧把正常邮件当垃圾。鉴于此情况我发信给公司全体人员，要求修改SMTP服务器，怕有些人不懂如何设置，我制作了3个版本的邮件客户端图文并貌的说明文件，但修改者寥寥，而我每天奔波在那些不仔细阅读说明的人中，筋疲力尽。要命的是总经理的不断问我，质问邮件为什么总发不出去，还收不到？？？</p><p>我哭了。</p><p>综合以上情况，最后不得不关闭了SL。</p><p>PS:在使用SL过程中还发现了诡异的幽灵记录</p><p>10-24 12:42:00 [27921]: [450 rejected, see [url]http://bl.extmail.org/cgi/freq?[/url] ,from=&lt;&gt; to=&lt;&gt; helo=&lt;&gt; client=&lt;&gt;]<br />10-24 13:05:24 [3926]: [450 rejected, see [url]http://bl.extmail.org/cgi/freq?[/url] ,from=&lt;&gt; to=&lt;&gt; helo=&lt;&gt; client=&lt;&gt;]<br />10-24 13:05:51 [3927]: [450 rejected, see [url]http://bl.extmail.org/cgi/freq?[/url] ,from=&lt;&gt; to=&lt;&gt; helo=&lt;&gt; client=&lt;&gt;]<br />10-24 13:05:57 [4006]: [450 rejected, see [url]http://bl.extmail.org/cgi/freq?[/url] ,from=&lt;&gt; to=&lt;&gt; helo=&lt;&gt; client=&lt;&gt;]<br />10-24 13:11:54 [6492]: [450 rejected, see [url]http://bl.extmail.org/cgi/freq?[/url] ,from=&lt;&gt; to=&lt;&gt; helo=&lt;&gt; client=&lt;&gt;]<br />10-24 13:12:13 [6499]: [450 rejected, see [url]http://bl.extmail.org/cgi/freq?[/url] ,from=&lt;&gt; to=&lt;&gt; helo=&lt;&gt; client=&lt;&gt;]<br />10-24 13:12:19 [6523]: [450 rejected, see [url]http://bl.extmail.org/cgi/freq?[/url] ,from=&lt;&gt; to=&lt;&gt; helo=&lt;&gt; client=&lt;&gt;]</p><p>在SL版问过也没人解答, 觉得这是SL的BUG. 因为在APF的日志中没有提交空信息的记录。</p><p>如果要在公司现有环境中部署反垃圾邮件，且不需要用户更改设置，就必须建立一套自己的反垃圾规则。经过两周的测试，总结了一些经验，也搜集到足够的资料。下一步打算开发一套自己的反垃圾系统。这样可以不再受第三方规则的困扰，将邮件控制权把握在自己手中(并不是说SL不好，也许是自己太笨，没弄明白)。</p><p>这个系统的雏形已经实现(VB6下的多线程模式)，和SL的工作原理一样，可以说是WINDOWS下的Spam Locker。只是判断规则按照内部需要自己完善。我的PERL很弱，否则就可以在SL的基础上修改一下。经过4天的测试，运行十分稳定，识别效率和效果也非常好。吸取前次部署SL的经验，这次打算测试一个模块，上线一个模块，发现问题马上调整。</p><p>反垃圾邮件的是痛苦的，不可能一步到位，最痛苦的是要面对用户的指责，质疑。也许我功力不够，也许是降大任前的苦其心志。我依旧奔在反垃圾的路上。</p><p>如果能够顺利到达终点，我会把相关资料整理成详细文挡，希望后来的兄弟不要象我般如此痛苦。</p>]]></description><category>Anti Spam</category><comments>http://blog.bigcomic.com/post/192.html#comment</comments><wfw:comment>http://blog.bigcomic.com/</wfw:comment><wfw:commentRss>http://blog.bigcomic.com/feed.asp?cmt=192</wfw:commentRss><trackback:ping>http://blog.bigcomic.com/cmd.asp?act=tb&amp;id=192&amp;key=6bf7d41e</trackback:ping></item></channel></rss>
