diff -uPr qmail-1.03-va/Makefile qmail-1.03/Makefile
--- qmail-1.03-va/Makefile	2005-08-08 11:43:07.000000000 +0200
+++ qmail-1.03/Makefile	2007-03-02 19:39:47.000000000 +0100
@@ -1536,13 +1536,15 @@
 timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \
 date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \
 open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \
-fs.a auto_qmail.o socket.lib
+fs.a auto_qmail.o socket.lib dns.o dns.lib lock.a timeoutconn.o \
+ndelay.a byte_zero.o
 	./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \
 	timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \
 	received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
 	datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
 	alloc.a substdio.a error.a str.a fs.a auto_qmail.o  `cat \
-	socket.lib`
+	socket.lib` dns.o `cat dns.lib` lock.a timeoutconn.o \
+	ndelay.a byte_zero.o
 
 qmail-smtpd.0: \
 qmail-smtpd.8
diff -uPr qmail-1.03-va/qmail-smtpd.c qmail-1.03/qmail-smtpd.c
--- qmail-1.03-va/qmail-smtpd.c	2005-08-08 11:43:15.000000000 +0200
+++ qmail-1.03/qmail-smtpd.c	2007-03-02 22:25:53.000000000 +0100
@@ -85,6 +85,457 @@
 stralloc helohost = {0};
 char *fakehelo; /* pointer into helohost, or 0 */
 
+/* smtpd_rlchk 20070303
+How it works for now:
+
+if env_var NOLOCALCHK is set, return OK
+if any check gives EACCESS error return OK
+= we have address USER@DOMAIN
+if domain not in control/locals check control/virtualdomains
+ if domain in control/virtualdomains get prefix (DOMAIN:PREFIX)
+= note, that if domain is in control/locals, control/virtualdomains is not check
+= here I consider address as local delivery (which may be false?)
+open and search users/cdb:
+if (PREFIX is set) {
+    if !PREFIX-user found return OK
+    if !PREFIX- found {
+        get HOME field
+        if exist /HOME/.qmail-USER or exist /HOME/.qmail-default return OK
+    }
+} else PREFIX is no set {
+    if !USER found return OK
+    if !USER- found {
+        get HOME field
+        if exist /HOME/.qmail-USER or exist /HOME/.qmail-default return OK
+    }
+}
+close users/cdb
+if (PREFIX is set) {
+    if exist ~PREFIX/.qmail-USER or exist ~PREFIX/.qmail-default return OK
+    if exist ~ALIAS/.qmail-PREFIX-USER or exist ~ALIAS/.qmail-PREFIX-default return OK
+} else PREFIX is no set {
+    if USER exist return OK
+    if exist ~ALIAS/.qmail-USER or exist ~alias/.qmail-default return OK
+}
+if USER contain '-'(ie SOME-EXT), strip last part and check again(ie SOME)
+*/
+/* TODO
+ * #include *.h instead *.c
+ * multi-line smtp responce, take first not last
+ * strncpy(), strrchr()
+ */
+#include <syslog.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include "dns.h"
+#include "auto_usera.c"
+#include "ndelay.h"
+#include "timeoutconn.h"
+#ifndef NULL
+#define NULL ((void*)0)
+#endif
+#define FEXIST(fn,sb) ( (!stat(fn, &sb)&&(sb.st_mode&S_IFREG)) || errno==EACCES )
+#define SPAMLOOPV "smtp.bounce.chk"
+#define SPAMLOOPL 12
+int seenspamloop = 0;
+char tmbuf[512];
+/* Our errors */
+void err_sls() { out("553 <" SPAMLOOPV "@...> seen, will not accept data command\r\n"); };
+int err_lau(const char *s) { out("550 <"); out(s); out(">: local address unknown\r\n"); return 0; };
+int err_dns(const char *h, const char *c) { out(c); out(" cant find MX for "); out(h); out("\r\n"); return 0; };
+int err_os() { out("451 out of system resources\r\n"); return 0; };
+/* int err_conn(const char *s, const char *ip) {
+	out("450 "); out(s); out(": cant connect to mx at "); out(ip); out("\r\n");
+	syslog(LOG_MAIL|LOG_NOTICE, "smtpdefer <%s>: cant connect to mx at %s\n", s, ip);
+	return 0;
+}; */
+int err_disc(const char *s, const char *ip, const char *a) { 
+	out("450 the mx for "); out(s); out(" at "); out(ip); out(" has disconected unexpectedly\r\n");
+	syslog(LOG_MAIL|LOG_NOTICE, "smtpdefer <%s>: mx at %s has disconected after %s\n", s, ip, a);
+	return 0;
+};
+int err_bads(const char *s, const char *ip, const char *r) {
+	out(*r == '5' ? "5" : "4");
+	out("50 checking bounce <"); out(s); out("> on "); out(ip);
+	out(" gave: "); out(r); /* out("\r\n"); is in r==responce */
+	syslog(LOG_MAIL|LOG_NOTICE, "%s <%s>, %s said: %s",
+		(*r == '5' ? "smtpreject" : "smtpdefer"), s, ip, r);
+	return 0;
+};
+/* replace all <f> with <t> */
+int str_rep(char *s, char f, char t) {
+int i = 0;
+    if (! s) return 0;
+    for (; *s; s++) if (*s == f) *s = t,i++;
+    return i;
+};
+/* return duplicate of <s> */
+char *str_dup(const char *s) {
+char *t = alloc(str_len(s)+1);
+    if (t) str_copy(t, s);
+    return t;
+};
+
+/* file read caching */
+#define c_reset(t)	{ cachesize=-1; cachetim = t; }
+static int	cachepos = 0;
+static int	cachesize = 0;
+static int	cachetim = 0;
+int c_fgets(int fd, char *buf, int size) {
+static char	cache[4080];
+char		*s = buf;
+    while (--size > 0) {	/* last byte for \0 */
+	if (cachepos >= cachesize) {
+	    cachesize = (cachetim>0
+			? timeoutread(cachetim, fd, cache, 4080)
+			: read(fd, cache, 4080) );
+	    if (cachesize <= 0) break;
+	    cachepos = 0;
+	};
+	*s = cache[cachepos++];
+	if (*s++ == 10) break;
+    };
+    *s = 0;
+    return s-buf;
+};
+
+/* cache remote rcpts */
+int checkcache(const char *rcpt, const char *host) {
+int fd, i;
+    if (snprintf(tmbuf, 500, "/tmp/%s/%s", SPAMLOOPV, host) < 13) return 0;
+    case_lowers(tmbuf);
+    if ((fd=open_read(tmbuf)) < 0) return 0;
+    c_reset(0);
+    while (c_fgets(fd, tmbuf, 256))
+	if (tmbuf[0] == '@' || /* is on whitelist */
+    	    !case_diffb(rcpt, host-rcpt, tmbuf))
+		{ close(fd); return 1; };
+    close(fd);
+    return 0;
+};
+
+int writecache(const char *rcpt, const char *host) {
+int fd;
+    if (snprintf(tmbuf, 500, "/tmp/%s/%s", SPAMLOOPV, host) < 13) return 0;
+    /* mkdir(path, 0700); */
+    case_lowers(tmbuf);
+    if ((fd=open_append(tmbuf)) < 0) return 0;
+    if (lock_ex(fd)) { close(fd); return 0; };
+    write(fd, rcpt, host-rcpt);
+    write(fd, tmbuf, sprintf(tmbuf, "%d\n", time(NULL)));
+    return close(fd);
+};
+
+/* find my default domain, cache the result */
+char *defaultdomain() {
+static char domain[255] = {0};
+int	fd, i;
+    if (domain[0]) return domain;
+    fd = open_read("control/defaultdomain");
+    if (fd < 0) fd = open_read("control/me");
+    if (fd < 0) fd = open_read("/proc/sys/kernel/domainname");
+    if (fd >= 0) {
+	c_reset(0);
+	if ((i = c_fgets(fd, domain, 250)) && domain[--i] == 10) domain[i] = 0;
+	close(fd);
+    };
+    if (domain[0]) return domain;
+    str_copy(domain, "unknown");
+    if (domain[0]) return domain;
+    return NULL;
+};
+
+/* if host is in locals, returns 0 if not */
+int islocal(const char *host) {
+int	i, fd = open_read("control/locals");
+    if (fd >= 0) {
+	c_reset(0);
+	while (i = c_fgets(fd, tmbuf, 500)) {
+	    if (tmbuf[--i] == 10) tmbuf[i] = 0;
+	    if (! str_diff(tmbuf, host)) {
+	        close(fd); return 1;
+	    };
+	};
+	close(fd);
+    };
+    return 0;
+};
+
+/* return prefix from virtualdomains or null */
+char *virtualhost(const char *host) {
+int	i, hostlen = str_len(host);
+char	*prefix = NULL;
+int	fd = open_read("control/virtualdomains");
+    if (fd >= 0) {
+	c_reset(0);
+	while (i = c_fgets(fd, tmbuf, 500)) {
+	    if (hostlen < i && tmbuf[hostlen] == ':' && !str_diffn(tmbuf, host, hostlen)) {
+		while (i && (tmbuf[--i] < 32)) tmbuf[i] = 0; /* strip \r\n, it will stop at : */
+		if (*(tmbuf+hostlen+1) == 0) break;
+		prefix = (char*)str_dup(tmbuf+hostlen+1);
+		break;
+	    };
+	};
+	close(fd);
+    };
+    return prefix;
+};
+
+int islocaluser(const char *user, const char *prefix) {
+struct stat stb;
+struct passwd *pwd;
+int	userlen, count, fd;
+int	isin = 0;
+char	*home, *tmp;
+char	puser[512] = {0};
+char	buf[1024] = {0};
+/* TODO - other wildchars than single - */
+    fd = open_read("users/cdb");
+    if (fd >= 0) {
+	if (prefix) userlen = snprintf(puser, 511, "!%s-%s", prefix, user);
+	else 	    userlen = snprintf(puser, 511, "!%s", user);
+	if (cdb_seek(fd, puser, userlen+1, &count) <= 0) {
+	    /* try alias */
+	    userlen = snprintf(puser, 511, "!%s-", prefix ? prefix : user);
+	    if (cdb_seek(fd, puser, userlen, &count) > 0 && count<1024) {
+		/* alias found, check ~aliashome/.qmail-* */
+		cdb_bread(fd, buf, count); //buf[count] = 0;
+		/* cdb data: user\0uid\0gid\0home\0... */
+		for (tmp = buf + count, home = buf, count = 0; count < 3 && home < tmp;)
+		    if (*home++ == 0) count++;
+		if (count == 3) {
+		    /* copy home to the start of buf, then append .qmail-? */
+		    count = str_copy(buf, home);	/* safe */
+		    tmp = buf+count;
+		    count = 1023 - count;
+		    snprintf(tmp, count, "/.qmail-%s", user);
+		    str_rep(tmp+8, '.', ':');
+		    if (FEXIST(buf, stb)) isin = 1;
+		    else {
+		     snprintf(tmp, count, "/.qmail-default");
+		     if (FEXIST(buf, stb)) isin = 1;
+		    };
+		};
+	    };
+	} else {
+	    /* user found */
+	    isin = 1;
+	};
+	close(fd);
+    }; /* if open(users/cdb) */
+    if (isin) return 1;
+
+    /* check filesystem now */
+    pwd = getpwnam(auto_usera);
+    if (prefix) {
+	/* ~alias/.qmail-prefix-user (TODO should i really check it ?) */
+	count = snprintf(buf, 1023, "%s/.qmail-%s-", pwd->pw_dir, prefix);
+	strncpy(buf+count, user, 1023-count);
+	str_rep(buf+count, '.', ':');
+	if (FEXIST(buf, stb)) return 1;
+	/* ~alias/.qmail-prefix-default */
+	strncpy(buf+count, "default", 1023-count);
+	if (FEXIST(buf, stb)) return 1;
+	if (pwd = getpwnam(prefix)) {
+	    /* ~prefix/.qmail-user */
+	    count = snprintf(buf, 1023, "%s/.qmail-", pwd->pw_dir);
+	    strncpy(buf+count, user, 1023-count);
+	    str_rep(buf+count, '.', ':');
+	    if (FEXIST(buf, stb)) return 1;
+	    /* ~prefix/.qmail-default */
+	    strncpy(buf+count, "default", 1023-count);
+	    if (FEXIST(buf, stb)) return 1;
+	};
+    } else {
+	/* ~alias/.qmail-user */
+	count = snprintf(buf, 1023, "%s/.qmail-", pwd->pw_dir);
+	strncpy(buf+count, user, 1023-count);
+	str_rep(buf+count, '.', ':');
+	if (FEXIST(buf, stb)) return 1;
+	/* ~alias/.qmail-default */
+	strncpy(buf+count, "default", 1023-count);
+	if (FEXIST(buf, stb)) return 1;
+	/* user exist */
+	if (getpwnam(user)) return 1;
+    };
+    return 0;
+};
+
+/* returns -1 == error (should not happen)
+	    0 == local non exist 
+	    1 == local OK (or SPAMLOOPV)
+	    2 == remote */
+int smtpd_local_addr(const char *rcpt) {
+char	*tmp = NULL;
+char	*prefix = NULL;
+char	*host = NULL;
+char	*user = NULL;
+
+    /* check nospam loop */
+    if (! str_diffn(rcpt, SPAMLOOPV, SPAMLOOPL)) return seenspamloop=1;
+
+    if (!(user = (char*)str_dup(rcpt))) return -1;
+    case_lowers(user);
+    /* strip to user@host */
+    if (tmp = (char*)strrchr(user, '@')) {
+	*tmp++ = 0;
+	host = (*tmp ? tmp : defaultdomain());
+    } else {
+	host = defaultdomain();
+    };
+    if (!host) { alloc_free(user); return -1; };
+
+    /* check if host is local or virtualdomains */
+    if (!islocal(host) && !(prefix = virtualhost(host))) {
+	alloc_free(user);
+	return 2;	/* propably remote */
+    };
+
+    /* now if addr exist locally, then if user has '-', strip last part */
+    do {
+	if (islocaluser(user, prefix)) {
+	    alloc_free(user); alloc_free(prefix);
+	    return 1;	/* OK */
+	};
+	for (tmp = user + str_len(user); --tmp > user; )
+	    if (*tmp == '-') { *tmp = 0; break; };
+    } while (tmp > user);
+
+    alloc_free(user); alloc_free(prefix);
+    return 0;	/* local && not found */
+};
+
+int smtpd_remote_addr(const char *mfrom) { /* + print error */
+char	*tmp = NULL;
+char	*host = NULL;
+stralloc sa = {0};
+ipalloc mxip = {0};
+struct ip_address *ipsort[11] = {0}; //10 usable mx's, ending with null
+unsigned int r, i, j, d_addr;
+char	ips[18] = {0}; /* 111.222.333.444\0 */
+
+    /* fix <>&<#@[]> == bounce&doublebounce, argh ! */
+    if (*mfrom == 0 || !str_diff("#@[]", mfrom)) return 1;
+    switch (smtpd_local_addr(mfrom)) {
+	case -1: die_control(); /* exits */
+	case 0: return err_lau(mfrom);
+	case 1: return 1;
+    };
+    /* if we relay, why check remote? */ 
+    if (env_get("RELAYCLIENT")) return 1; 
+    /* propably 2, which means remote */
+    /* strip to user@host */
+    if (host = (char*)strrchr(mfrom, '@'))
+	host++;
+    else
+	return err_lau(mfrom);
+    /* check cache */
+    if (checkcache(mfrom, host)) return 1;
+
+    r = now() + (getpid() << 16);
+    stralloc_copys(&sa, host);
+    dns_init(0);
+    switch (dns_mxip(&mxip,&sa,r)) {
+	case DNS_MEM: die_nomem();
+	case DNS_HARD: return err_dns(host, "553");
+	case DNS_SOFT: return err_dns(host, "451");
+	default: if (mxip.len <= 0) return err_dns(host, "451");
+    };
+    //sort IP's
+    for(j = 0; j < 10; ++j) {
+	r = 0x1FFFFFFF; //min preference
+        d_addr = 0; //index of min mx
+	for (i = 0; i < mxip.len; i++)
+	    /* if (ipme_is(&ip.ix[i].ip)) TODO: maybe i should keep it ? */
+	    if (mxip.ix[i].pref < r) {
+	        r = mxip.ix[i].pref; d_addr = i;
+	    };
+	if (r == 0x1FFFFFFF) break; //no more MXes
+	ipsort[1+j] = NULL;
+	ipsort[j] = &mxip.ix[d_addr].ip;
+	mxip.ix[d_addr].pref = 0x1FFFFFFF; //mark this done
+    }
+
+    defaultdomain(); /* it may use c_reset() on first call */
+    c_reset(16); //wait 16seconds for every response
+    //check every IP
+    for(d_addr = 0; ipsort[d_addr]; ++d_addr)
+    {
+	r = socket(AF_INET,SOCK_STREAM,0);
+	if (r < 0) return err_os();
+	ips[ip_fmt(ips, ipsort[d_addr])] = 0; //human-readable IP
+	if (timeoutconn(r, ipsort[d_addr], 25, 31)) { //31 seconds
+	    close(r);
+	    continue; //try next IP
+	    //return err_conn(mfrom, ips);
+	};
+        /* do smtp conversation */
+        while((i=c_fgets(r, tmbuf, 500))>3 && tmbuf[3]=='-') ;
+	if (i < 4) { //need 220\n at least
+	    close(r);
+	    continue; //try next IP, possibly disconnected
+	    //return err_disc(mfrom, ips, "CONN");
+	};
+	if (tmbuf[0] != '2') {
+	    close(r);
+	    return err_bads(mfrom, ips, tmbuf);
+	};
+        //from now on, errors are permanent
+	i = snprintf(tmbuf, 500, "HELO %s\r\n", defaultdomain());
+        timeoutwrite(5, r, tmbuf, i); fsync(r);
+        while((i=c_fgets(r, tmbuf, 500))>3 && tmbuf[3]=='-') ;
+	if (i < 4) { close(r); return err_disc(mfrom, ips, "HELO"); };
+        if (tmbuf[0] != '2') {
+    	    close(r);
+	    return err_bads(mfrom, ips, tmbuf);
+	};
+	i = snprintf(tmbuf, 500, "MAIL FROM: <" SPAMLOOPV "@%s>\r\n", defaultdomain());
+	timeoutwrite(5, r, tmbuf, i); fsync(r);
+	while((i=c_fgets(r, tmbuf, 500))>3 && tmbuf[3]=='-') ;
+	if (i < 4) { close(r); return err_disc(mfrom, ips, "MFROM"); };
+        if (tmbuf[0] != '2') {
+	    close(r);
+	    return err_bads(mfrom, ips, tmbuf);
+	};
+	/* check real mfrom */
+	i = snprintf(tmbuf, 500, "RCPT TO: <%s>\r\n", mfrom);
+        timeoutwrite(5, r, tmbuf, i); fsync(r);
+	while((i=c_fgets(r, tmbuf, 500))>3 && tmbuf[3]=='-') ;
+        if (i < 4) { close(r); return err_disc(mfrom, ips, "RTO"); };
+	if (tmbuf[0] != '2') {
+	    close(r);
+	    return err_bads(mfrom, ips, tmbuf);
+	};
+        /* check whether rubbis is accepted */ 
+	i = snprintf(tmbuf, 500, "RCPT TO: <any." SPAMLOOPV "@%s>\r\n", host); 
+        timeoutwrite(5, r, tmbuf, i); fsync(r); 
+	while((i=c_fgets(r, tmbuf, 500))>3 && tmbuf[3]=='-') ; 
+        /* if (i < 4) { close(r); return err_disc(mfrom, ips, "RTOA"); }; */
+	if (i < 4) { close(r); tmbuf[0] = '5'; }; /* assume we can't */
+     
+        writecache((tmbuf[0] == '2' ? host-1 : mfrom), host);
+
+	timeoutwrite(1, r, "QUIT\r\n", 6); fsync(r);
+        c_fgets(r, tmbuf, 500); //get whatever there is
+	close(r);
+        return 1;
+    }; //for d_addr
+    
+    //no MX could be connected, defer this mail
+    tmbuf[0] = 0; //max 10 ips each 16 chars long == 160 chars
+    for(d_addr = 0; ipsort[d_addr]; ++d_addr)
+    {
+	ips[ip_fmt(ips, ipsort[d_addr])] = 0; //human-readable IP
+	strcat(tmbuf, ips); strcat(tmbuf, " ");
+    }
+    out("450 "); out(mfrom); out(": cant connect to any mx, tried "); out(tmbuf); out("\r\n");
+    syslog(LOG_MAIL|LOG_NOTICE, "smtpdefer <%s>: cant connect to any mx, tried %s\n", mfrom, tmbuf);
+    return 0;
+};
+/* smtpd_rlchk */
+
 void dohelo(arg) char *arg; {
   if (!stralloc_copys(&helohost,arg)) die_nomem(); 
   if (!stralloc_0(&helohost)) die_nomem(); 
@@ -234,6 +685,7 @@
 }
 void smtp_rset()
 {
+  seenspamloop = 0; /* smtpd_rlchk */
   seenmail = 0;
   out("250 flushed\r\n");
 }
@@ -258,6 +710,7 @@
   }
   else
     if (!addrallowed()) { err_nogateway(); return; }
+  if (!env_get("NOLOCALCHK") && !smtpd_local_addr(addr.s)) { err_lau(addr.s); return; }; /* smtpd_rlchk */
   if (!stralloc_cats(&rcptto,"T")) die_nomem();
   if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
   if (!stralloc_0(&rcptto)) die_nomem();
@@ -372,6 +825,8 @@
  
   if (!seenmail) { err_wantmail(); return; }
   if (!rcptto.len) { err_wantrcpt(); return; }
+  if (!seenspamloop && !env_get("NOREMOTECHK") && !smtpd_remote_addr(mailfrom.s)) { return; }; /* smtpd_rlchk */
+  if ( seenspamloop) { err_sls(); return; }; /* smtpd_rlchk */
   seenmail = 0;
   if (databytes) bytestooverflow = databytes + 1;
   if (qmail_open(&qqt) == -1) { err_qqt(); return; }
