diff -ruN netqmail-1.05/dbdef.sql netqmail-1.05-greylisting/dbdef.sql
--- netqmail-1.05/dbdef.sql	1970-01-01 01:00:00.000000000 +0100
+++ netqmail-1.05-greylisting/dbdef.sql	2007-10-24 00:11:26.456554000 +0200
@@ -0,0 +1,33 @@
+--
+-- (c) GPL 2007 Robert Sander <gurubert@gurubert.de> 
+--
+
+CREATE TABLE relaytofrom (
+    id bigserial NOT NULL,
+    relay_ip inet,
+    mail_from character varying(255),
+    rcpt_to character varying(255),
+    block_expires timestamp with time zone DEFAULT '-infinity'::timestamp with time zone NOT NULL,
+    record_expires timestamp with time zone DEFAULT 'infinity'::timestamp with time zone NOT NULL,
+    blocked_count bigint DEFAULT 0::bigint NOT NULL,
+    passed_count bigint DEFAULT 0::bigint NOT NULL,
+    aborted_count bigint DEFAULT 0::bigint NOT NULL,
+    origin_type character(1) DEFAULT 'M'::bpchar NOT NULL,
+    create_time timestamp with time zone DEFAULT now() NOT NULL,
+    last_update timestamp with time zone DEFAULT now() NOT NULL
+);
+
+CREATE INDEX block_expires_index ON relaytofrom USING btree (block_expires);
+
+CREATE INDEX mail_from_index ON relaytofrom USING btree (mail_from);
+
+CREATE INDEX origin_type_index ON relaytofrom USING btree (origin_type);
+
+CREATE INDEX rcpt_to_index ON relaytofrom USING btree (rcpt_to);
+
+CREATE INDEX record_expires_index ON relaytofrom USING btree (record_expires);
+
+CREATE INDEX relay_ip_index ON relaytofrom USING btree (relay_ip);
+
+ALTER TABLE ONLY relaytofrom
+    ADD CONSTRAINT relaytofrom_pkey PRIMARY KEY (id);
diff -ruN netqmail-1.05/FILES netqmail-1.05-greylisting/FILES
--- netqmail-1.05/FILES	2007-10-24 08:48:47.709929827 +0200
+++ netqmail-1.05-greylisting/FILES	2007-10-24 08:49:07.342649089 +0200
@@ -432,3 +432,7 @@
 tcp-environ.5
 constmap.h
 constmap.c
+local_scan.c
+qmail-envelope-scanner.c
+dbdef.sql
+local_scan.h 
diff -ruN netqmail-1.05/hier.c netqmail-1.05-greylisting/hier.c
--- netqmail-1.05/hier.c	1998-06-15 12:53:16.000000000 +0200
+++ netqmail-1.05-greylisting/hier.c	2007-10-24 08:49:07.346649235 +0200
@@ -127,6 +127,7 @@
   c(auto_qmail,"bin","qmail-qmqpd",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","qmail-qmtpd",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","qmail-smtpd",auto_uido,auto_gidq,0755);
+  c(auto_qmail,"bin","qmail-envelope-scanner",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","sendmail",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755);
diff -ruN netqmail-1.05/install-big.c netqmail-1.05-greylisting/install-big.c
--- netqmail-1.05/install-big.c	1998-06-15 12:53:16.000000000 +0200
+++ netqmail-1.05-greylisting/install-big.c	2007-10-24 08:49:07.346649235 +0200
@@ -127,6 +127,7 @@
   c(auto_qmail,"bin","qmail-qmqpd",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","qmail-qmtpd",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","qmail-smtpd",auto_uido,auto_gidq,0755);
+  c(auto_qmail,"bin","qmail-envelope-scanner",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","sendmail",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","tcp-env",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","qreceipt",auto_uido,auto_gidq,0755);
diff -ruN netqmail-1.05/local_scan.c netqmail-1.05-greylisting/local_scan.c
--- netqmail-1.05/local_scan.c	1970-01-01 01:00:00.000000000 +0100
+++ netqmail-1.05-greylisting/local_scan.c	2007-10-23 23:47:59.778063000 +0200
@@ -0,0 +1,342 @@
+/**************
+ * Copyright (c) mjd@digitaleveryware.com 2003 
+ * GPL Licensed see http://www.gnu.org/licenses/gpl.html
+ * Version 0.04: I don't even have it in cvs yet 
+ * ChangeHistory:
+ * Version 0.05: support for IPv6 using PostgreSQL <gurubert@gurubert.de>
+ * Version 0.04: support for qmail
+ * Version 0.03: added whitelisting by incoming domain address
+ * Version 0.02: added relay_ip matching by /24 subnet
+ *
+ *************/
+
+#include "local_scan.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "libpq-fe.h"
+
+/************************/
+#define DEFAULT_DBHOST NULL
+#define DEFAULT_DBUSER NULL
+#define DEFAULT_DBPASS NULL
+#define DEFAULT_DB     "relaydelay"
+#define DEFAULT_BLOCK_EXPIRE  45   /* minutes until email is accepted */
+#define DEFAULT_RECORD_EXPIRE 500  /* minutes until record expires */
+#define DEFAULT_RECORD_EXPIRE_GOOD  72 /* days until record expires after accepting email */
+#define DEFAULT_LOCAL_SCAN_DEBUG   0 /* set to 1 to enable debugging */
+
+/*************************/
+
+#define SQLCMDSIZE 1024
+
+char *dbhost, *dbuser, *dbpass, *db;
+int block_expire, record_expire, record_expire_good, local_scan_debug;
+
+PGresult *pg_query_wrapper(PGconn *pgconn, char *sqltext, char *debugmsg) 
+{
+	PGresult *result;
+
+	result = PQexec(pgconn, sqltext);
+	if(local_scan_debug == 1)
+		fprintf(stderr,
+		        "local_scan: %s SQL: |%s| result status: '%s'\n",
+		        debugmsg,
+		        sqltext,
+		        PQresStatus(PQresultStatus(result)));
+	return result;
+}
+
+int convert_true_false(char *input) {
+	if (!strcmp(input, "t"))
+		return TRUE;
+	return FALSE;
+}
+
+/* returns TRUE FALSE if record exists in database */
+int checkWhiteListIP(PGconn *pgconn, int *action) {
+	char sql[SQLCMDSIZE], sid[32];
+	int white, black, exists = FALSE;
+	PGresult *result;
+	int row;
+	
+  /* check for whitelisted sender ip address */
+	sprintf(sql,
+	        "SELECT id, block_expires > NOW(), block_expires < NOW() FROM relaytofrom WHERE record_expires > NOW() AND mail_from IS NULL AND rcpt_to IS NULL AND relay_ip >>= '%s' ORDER BY masklen(relay_ip)",
+	       sender_host_address);
+	
+	result = pg_query_wrapper(pgconn,sql,"IP");
+	if(PQresultStatus(result) == PGRES_TUPLES_OK) {
+		for (row = 0; row < PQntuples(result); row++) {
+			exists = TRUE;
+			strncpy(sid, PQgetvalue(result, row, 0), sizeof(sid));
+			black = convert_true_false(PQgetvalue(result, row, 1));
+			white = convert_true_false(PQgetvalue(result, row, 2));
+		}
+	}
+	PQclear(result); // free memory used by result
+	if(exists && white) {
+		fprintf(stderr,
+		        "local_scan: %s -> xxx (%s) Whitelist IP Accept id = %s\n",
+		        sender_address,
+		        sender_host_address,
+		        sid);
+		*action = LOCAL_SCAN_ACCEPT;
+	}
+	else
+		if (exists && black) {
+			fprintf(stderr,
+			        "local_scan: %s -> xxx (%s) Blacklist IP Permanent Reject id = %s\n",
+			        sender_address,
+			        sender_host_address,
+			        sid);
+			*action = LOCAL_SCAN_REJECT;
+		}
+	return exists;
+}
+
+/* returns TRUE FALSE if record exists in database */
+int checkWhiteListDomain(PGconn *pgconn, int *action, int i) {
+	PGresult *result; 
+	int row;
+	
+	char sql[SQLCMDSIZE], sid[32], *indomain;
+	int white, black, exists = FALSE;
+	
+	indomain = strrchr(recipients_list[i].address,'@');
+	if(indomain != NULL) {
+		indomain++; /* skip the '@' char */
+		
+    /* check for whitelisted receiver domain address */
+		sprintf(sql,
+		        "SELECT id, block_expires > NOW(), block_expires < NOW() FROM relaytofrom WHERE record_expires > NOW() AND mail_from IS NULL AND relay_ip IS NULL AND rcpt_to = '%s'",
+		        indomain);
+		result = pg_query_wrapper(pgconn,sql,"domain");
+		if(PQresultStatus(result) == PGRES_TUPLES_OK) {
+			for (row = 0; row < PQntuples(result); row++) {
+				exists = TRUE;
+				strncpy(sid, PQgetvalue(result, row, 0), sizeof(sid));
+				black = convert_true_false(PQgetvalue(result, row, 1));
+				white = convert_true_false(PQgetvalue(result, row, 2));
+			}
+		}
+		PQclear(result);
+		if (exists && white) {
+			fprintf(stderr,
+			        "local_scan: %s -> %s (%s) Whitelist Domain Accept id = %s\n",
+			        sender_address,
+			        recipients_list[i].address,
+			        sender_host_address,
+			        sid);
+			*action = LOCAL_SCAN_ACCEPT;
+		}
+		else if (exists && black) {
+			fprintf(stderr,
+			        "local_scan: %s -> xxx (%s) Blacklist Domain Permanent Reject id = %s\n",
+			        sender_address,
+			        sender_host_address,
+			        sid);
+			*action = LOCAL_SCAN_REJECT;
+		}
+	}
+	if (!exists) {
+    /* check for whitelisted receiver address */
+		sprintf(sql,
+		        "SELECT id, block_expires > NOW(), block_expires < NOW() FROM relaytofrom WHERE record_expires > NOW() AND mail_from IS NULL AND relay_ip IS NULL AND rcpt_to = '%s'",
+		        recipients_list[i].address);
+		result = pg_query_wrapper(pgconn,sql,"address");
+		if(PQresultStatus(result) == PGRES_TUPLES_OK) {
+			for (row = 0; row < PQntuples(result); row++) {
+				exists = TRUE;
+				strncpy(sid, PQgetvalue(result, row, 0), sizeof(sid));
+				black = convert_true_false(PQgetvalue(result, row, 1));
+				white = convert_true_false(PQgetvalue(result, row, 2));
+			}
+		}
+		PQclear(result);
+		if (exists && white) {
+			fprintf(stderr,
+			        "local_scan: %s -> %s (%s) Whitelist Domain Accept id = %s\n",
+			        sender_address,
+			        recipients_list[i].address,
+			        sender_host_address,
+			        sid);
+			*action = LOCAL_SCAN_ACCEPT;
+		}
+		else if (exists && black) {
+			fprintf(stderr,
+			        "local_scan: %s -> xxx (%s) Blacklist Domain Permanent Reject id = %s\n",
+			        sender_address,
+			        sender_host_address,
+			        sid);
+			*action = LOCAL_SCAN_REJECT;
+		}
+	}
+	return exists;
+}
+
+void buildLookupSql(char *sql, int recipIndex) 
+{
+	if (! getenv("GREYLIST_ONLY_SUBNETS")) {
+    /* this first sprintf does an exact match on ipaddr */
+		sprintf(sql,
+		        "SELECT id, NOW() > block_expires FROM relaytofrom WHERE record_expires > NOW() AND mail_from = '%s' AND rcpt_to = '%s' AND relay_ip = '%s' order by block_expires desc",
+		        sender_address,
+		        recipients_list[recipIndex].address,
+		        sender_host_address);
+	} else {
+    /* this second version matches anything in the same /24 (IPv4) or /64 (IPv6) subnet */
+		sprintf(sql,
+		        "SELECT id, NOW() > block_expires FROM relaytofrom WHERE record_expires > NOW() AND mail_from = '%s' AND rcpt_to = '%s' AND family(relay_ip) = family('%s') and set_masklen(relay_ip, case family('%s') when 6 then 64 else 24 end) >>= '%s'",
+		        sender_address,
+		        recipients_list[recipIndex].address,
+		        sender_host_address,
+		        sender_host_address,
+		        sender_host_address
+		       );
+	}
+}
+
+int checkGreylist( PGconn *pgconn,  int *action, int i) 
+{
+	char sql[SQLCMDSIZE], sid[32];
+	int notexpired, exists = FALSE;
+	PGresult *result;
+	int row;
+	
+ // lookup to see if record exists
+	buildLookupSql(sql, i);
+	result = pg_query_wrapper(pgconn,sql,"greylist");
+	if(PQresultStatus(result) == PGRES_TUPLES_OK) {
+		for (row = 0; row < PQntuples(result); row++) {
+			exists = TRUE;
+			strncpy(sid, PQgetvalue(result, row, 0), sizeof(sid));
+			notexpired = convert_true_false(PQgetvalue(result, row, 1));
+		}
+		PQclear(result);
+   // if it exists and is not record expired and the block_expired passes
+		if (exists && notexpired) {
+     // update expire time to 36 days, and pass count
+			sprintf(sql,
+			        "update relaytofrom set record_expires = NOW() + '%d DAYS', passed_count = passed_count + 1, last_update = now() where id ='%s'",
+			        record_expire_good,
+			        sid);
+			result = pg_query_wrapper(pgconn, sql, "exists accept");
+			fprintf(stderr,
+			        "local_scan: %s -> %s (%s) Exists Accept id = %s expire = %d\n",
+			        sender_address,
+			        recipients_list[i].address,
+			        sender_host_address,
+			        sid,
+			        notexpired);
+			*action = LOCAL_SCAN_ACCEPT;
+		} else if ( exists && ! notexpired) {
+     // update fail count
+			sprintf(sql,
+			        "update relaytofrom set blocked_count = blocked_count + 1, last_update = now() where id = %s",
+			        sid);
+			result = pg_query_wrapper(pgconn, sql, "exists block");
+			fprintf(stderr,
+			        "local_scan: %s -> %s (%s) Exists Block id = %s expire = %d\n",
+			        sender_address,
+			        recipients_list[i].address,
+			        sender_host_address,
+			        sid,
+			        notexpired);
+			*action = LOCAL_SCAN_TEMPREJECT;
+		} else /* doesn't exist */ {
+     // it doesn't exist make and entry
+			sprintf(sql,
+			        "insert into relaytofrom (relay_ip, mail_from, rcpt_to, block_expires, record_expires, blocked_count, origin_type) values ('%s','%s','%s',NOW() + '%d MINUTES',NOW() + '%d MINUTES',1,'A')",
+			        sender_host_address,
+			        sender_address,
+			        recipients_list[i].address,
+			        block_expire,
+			        record_expire);
+			result = pg_query_wrapper(pgconn,sql,"doesn't exist block");
+			fprintf(stderr,
+			        "local_scan: %s -> %s (%s) Doesn't Exist Block\n",
+			        sender_address,
+			        recipients_list[i].address,
+			        sender_host_address);
+			*action = LOCAL_SCAN_TEMPREJECT;
+		}
+	}
+	PQclear(result);
+}
+
+void get_config(void) {
+
+	char *t;
+
+	dbhost = getenv("DBHOST");
+	if (!dbhost) {
+		dbhost = DEFAULT_DBHOST;
+	}
+	dbuser = getenv("DBUSER");
+	if (!dbuser) {
+		dbuser = DEFAULT_DBUSER;
+	}
+	dbpass = getenv("DBPASS");
+	if (!dbpass) {
+		dbpass = DEFAULT_DBPASS;
+	}
+	db = getenv("DB");
+	if (!db) {
+		db = DEFAULT_DB;
+	}
+	t = getenv("BLOCK_EXPIRE");
+	if (t) {
+		block_expire = atoi(t);
+	} else {
+		block_expire = DEFAULT_BLOCK_EXPIRE;
+	}
+	t = getenv("RECORD_EXPIRE");
+	if (t) {
+		record_expire = atoi(t);
+	} else {
+		record_expire = DEFAULT_RECORD_EXPIRE;
+	}
+	t = getenv("RECORD_EXPIRE_GOOD");
+	if (t) {
+		record_expire_good = atoi(t);
+	} else {
+		record_expire_good = DEFAULT_RECORD_EXPIRE_GOOD;
+	}
+	t = getenv("LOCAL_SCAN_DEBUG");
+	if (t) {
+		local_scan_debug = atoi(t);
+	} else {
+		local_scan_debug = DEFAULT_LOCAL_SCAN_DEBUG;
+	}
+}
+
+int local_scan(int fd, uschar **return_text)
+{
+	PGconn *pgconn = NULL;
+	int i, ret = LOCAL_SCAN_ACCEPT;
+	
+	get_config();
+	
+	if(local_scan_debug == 1) fprintf(stderr,"local_scan: protocol = %s %s  \n",received_protocol,sender_address);
+	
+	pgconn = PQsetdbLogin(dbhost, NULL, NULL, NULL, db, dbuser, dbpass);
+	if (PQstatus(pgconn)==CONNECTION_OK) {
+		if ( !checkWhiteListIP( pgconn, &ret )) {      /* check for whitelisted sender ip address */
+			for(i= 0; i <  recipients_count; i++) {
+				if(strlen(sender_host_address) + strlen(sender_address) +
+				   strlen(recipients_list[i].address) < SQLCMDSIZE - 200) { /* check to avoid buffer overflows */
+					   if(!checkWhiteListDomain( pgconn, &ret, i))
+						   checkGreylist(pgconn, &ret,i);
+				   }
+			}
+		}
+	} else {
+		fprintf(stderr, "local_scan: unable to connect to postgresql database %s@%s\n", db, dbhost);
+	}
+	if(pgconn) PQfinish(pgconn);
+	if(local_scan_debug == 1) fprintf(stderr, "local_scan: --------\n");
+	return ret;
+}
+
+/* End of local_scan.c */
diff -ruN netqmail-1.05/local_scan.h netqmail-1.05-greylisting/local_scan.h
--- netqmail-1.05/local_scan.h	1970-01-01 01:00:00.000000000 +0100
+++ netqmail-1.05-greylisting/local_scan.h	2007-10-24 08:49:07.350649382 +0200
@@ -0,0 +1,33 @@
+/**************
+ * Copyright (c) mjd@digitaleveryware.com 2003 
+ * GPL Licensed see http://www.gnu.org/licenses/gpl.html
+ * Version 0.04: I don't even have it in cvs yet 
+ * ChangeHistory:
+ * Version 0.04: support for qmail
+ * Version 0.03: added whitelisting by incoming domain address
+ * Version 0.02: added relay_ip matching by /24 subnet
+ *************/
+
+#define FALSE 0
+#define TRUE 1
+#define uschar char
+
+#define LOCAL_SCAN_ACCEPT  0
+#define LOCAL_SCAN_REJECT  100
+#define LOCAL_SCAN_TEMPREJECT  101
+#define SOMETHING_BAD      102
+
+struct recipients_list {
+  char *address;
+};
+
+
+int local_scan(int fd, uschar **return_text);
+
+/* globals used by exim */
+
+extern int recipients_count;
+extern char *received_protocol;
+extern char *sender_host_address;
+extern char *sender_address;
+extern struct recipients_list recipients_list[];
diff -ruN netqmail-1.05/Makefile netqmail-1.05-greylisting/Makefile
--- netqmail-1.05/Makefile	2007-10-24 08:48:47.713929973 +0200
+++ netqmail-1.05-greylisting/Makefile	2007-10-24 08:51:08.911100336 +0200
@@ -803,7 +803,7 @@
 predate datemail mailsubj qmail-upq qmail-showctl qmail-newu \
 qmail-pw2u qmail-qread qmail-qstat qmail-tcpto qmail-tcpok \
 qmail-pop3d qmail-popup qmail-qmqpc qmail-qmqpd qmail-qmtpd \
-qmail-smtpd sendmail tcp-env qmail-newmrh config config-fast dnscname \
+qmail-smtpd qmail-envelope-scanner sendmail tcp-env qmail-newmrh config config-fast dnscname \
 dnsptr dnsip dnsmxip dnsfq hostname ipmeprint qreceipt qsmhook qbiff \
 forward preline condredirect bouncesaying except maildirmake \
 maildir2mbox maildirwatch qail elq pinq idedit install-big install \
@@ -1544,6 +1544,18 @@
 	alloc.a substdio.a error.a str.a fs.a auto_qmail.o  `cat \
 	socket.lib`
 
+qmail-envelope-scanner: \
+load qmail-envelope-scanner.o local_scan.o /usr/lib/libmysqlclient.a
+	./load qmail-envelope-scanner -lz -lm -L`pg_config --libdir` -lpq local_scan.o
+
+qmail-envelope-scanner.o: \
+compile qmail-envelope-scanner.c local_scan.h
+	./compile qmail-envelope-scanner.c
+
+local_scan.o: \
+compile local_scan.c local_scan.h
+	./compile -I`pg_config --includedir` local_scan.c
+
 qmail-smtpd.0: \
 qmail-smtpd.8
 	nroff -man qmail-smtpd.8 > qmail-smtpd.0
@@ -1777,7 +1789,7 @@
 qmail-lspawn.c qmail-newmrh.c qmail-newu.c qmail-pop3d.c \
 qmail-popup.c qmail-pw2u.c qmail-qmqpc.c qmail-qmqpd.c qmail-qmtpd.c \
 qmail-qread.c qmail-qstat.sh qmail-queue.c qmail-remote.c \
-qmail-rspawn.c qmail-send.c qmail-showctl.c qmail-smtpd.c \
+qmail-rspawn.c qmail-send.c qmail-showctl.c qmail-smtpd.c qmail-envelope-scanner.c \
 qmail-start.c qmail-tcpok.c qmail-tcpto.c spawn.c dnscname.c dnsfq.c \
 dnsip.c dnsmxip.c dnsptr.c hostname.c ipmeprint.c tcp-env.c \
 sendmail.c qreceipt.c qsmhook.c qbiff.c forward.c preline.c predate.c \
diff -ruN netqmail-1.05/qmail-envelope-scanner.c netqmail-1.05-greylisting/qmail-envelope-scanner.c
--- netqmail-1.05/qmail-envelope-scanner.c	1970-01-01 01:00:00.000000000 +0100
+++ netqmail-1.05-greylisting/qmail-envelope-scanner.c	2007-10-24 08:49:07.358649675 +0200
@@ -0,0 +1,33 @@
+/**************
+ * Copyright (c) mjd@digitaleveryware.com 2003 
+ * GPL Licensed see http://www.gnu.org/licenses/gpl.html
+ * Version 0.04: I don't even have it in cvs yet 
+ * ChangeHistory:
+ * Version 0.04: support for qmail
+ * Version 0.03: added whitelisting by incoming domain address
+ * Version 0.02: added relay_ip matching by /24 subnet
+ *************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "local_scan.h"
+
+
+char *received_protocol;
+char *sender_host_address;
+char *sender_address;
+struct recipients_list recipients_list[1];
+int recipients_count = 1;
+
+int main(int argv, char *argc[])
+{
+  received_protocol = "notneeded4qmail";
+  sender_host_address = getenv("TCPREMOTEIP");
+  recipients_list[0].address = argc[2];
+  sender_address = argc[1];
+
+  if (argv == 3 && sender_host_address != NULL)
+    return local_scan(1,NULL);
+  else
+    return SOMETHING_BAD;
+}
diff -ruN netqmail-1.05/qmail-smtpd.c netqmail-1.05-greylisting/qmail-smtpd.c
--- netqmail-1.05/qmail-smtpd.c	2007-10-24 08:48:47.729930559 +0200
+++ netqmail-1.05-greylisting/qmail-smtpd.c	2007-10-24 08:49:07.358649675 +0200
@@ -19,6 +19,8 @@
 #include "env.h"
 #include "now.h"
 #include "exit.h"
+#include "fork.h"
+#include "wait.h"
 #include "rcpthosts.h"
 #include "timeoutread.h"
 #include "timeoutwrite.h"
@@ -49,6 +51,8 @@
 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_tempfail() { out("421 temporary envelope failure (#4.3.0)\r\n"); }
+void err_permfail() { out("553 sorry, permanent envelope failure (#5.7.1)\r\n"); }
 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(arg) char *arg; { out("502 unimplemented (#5.5.1)\r\n"); }
@@ -222,6 +226,37 @@
 stralloc mailfrom = {0};
 stralloc rcptto = {0};
 
+int envelope_scanner()
+{
+  int child;
+  int wstat;
+  char *envelope_scannerarg[] = { "bin/qmail-envelope-scanner", mailfrom.s, addr.s, 0 };
+
+  switch(child = vfork()) {
+    case -1:
+      return 1;
+    case 0:
+      execv(*envelope_scannerarg,envelope_scannerarg);
+      _exit(111);
+  }
+
+  wait_pid(&wstat,child);
+  if (wait_crashed(wstat)) {
+    return 1;
+  }
+
+  switch(wait_exitcode(wstat)) {
+    case 101:
+      err_tempfail();
+      return 0;
+    case 100:
+      err_permfail();
+      return 0;
+    default:
+      return 1;
+  }
+}
+
 void smtp_helo(arg) char *arg;
 {
   smtp_greet("250 "); out("\r\n");
@@ -256,8 +291,10 @@
     if (!stralloc_cats(&addr,relayclient)) die_nomem();
     if (!stralloc_0(&addr)) die_nomem();
   }
-  else
+  else {
     if (!addrallowed()) { err_nogateway(); return; }
+    if (!envelope_scanner()) return;
+  }
   if (!stralloc_cats(&rcptto,"T")) die_nomem();
   if (!stralloc_cats(&rcptto,addr.s)) die_nomem();
   if (!stralloc_0(&rcptto)) die_nomem();
diff -ruN netqmail-1.05/TARGETS netqmail-1.05-greylisting/TARGETS
--- netqmail-1.05/TARGETS	1998-06-15 12:53:16.000000000 +0200
+++ netqmail-1.05-greylisting/TARGETS	2007-10-24 08:49:07.358649675 +0200
@@ -385,3 +385,6 @@
 man
 setup
 check
+local_scan.o
+qmail-envelope-scanner.o
+qmail-envelope-scanner
