diff -Nru netqmail-1.06_errmsg/grey.c netqmail-1.06_errmsg_grey1.15/grey.c
--- netqmail-1.06_errmsg/grey.c	1970-01-01 01:00:00.000000000 +0100
+++ netqmail-1.06_errmsg_grey1.15/grey.c	2009-08-28 00:13:52.000000000 +0100
@@ -0,0 +1,72 @@
+#include "stralloc.h"
+#include "ip.h"
+#include "str.h"
+#include "ndelay.h"
+#include "grey.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include "error.h"
+#include "scan.h"
+
+/* check greylist server to see whether to accept this message */
+
+static int grey_initialised=0;
+static int sockfd;
+
+void grey_init(svr,errfn) /* errfn must _exit */
+char *svr; void (*errfn)();
+{
+  struct ip_address ip;
+  unsigned int port;
+
+  if (scan_ip_port(svr,DEFAULTGREYIP,DEFAULTGREYPORT,&ip,&port) == -1) (*errfn)();
+  sockfd = connect_udp(ip,port,errfn);
+  grey_initialised=1;
+}
+
+/* Check given greylist triple: Pass connectingip+from+tolist to greydaemon on IP
+ * address gip. timeoutfn and errfn passed in case of error. Note that greycheck
+ * may be called more than once during a single SMTP session (= qmail-smtpd instance).
+ */
+int greycheck(gip, connectingip, from, tolist, tolen, timeoutfn, errfn) /* errfn must _exit */
+char *gip, *connectingip, *from, *tolist;
+int tolen;
+void (*timeoutfn)(), (*errfn)();
+{
+  int r;
+  static stralloc chkpacket = {0};
+  char rbuf[2];
+
+  if (!gip) (*errfn)(); /* greycheck should only be called if gip set */
+
+  if (!grey_initialised) grey_init(gip,errfn);
+  
+/* ndelay_on - dubious benefit with UDP, may even slow things down so disabled */
+/*ndelay_on(sockfd); */
+
+  if (!stralloc_copys(&chkpacket,"I")) die_nomem();
+  if (!stralloc_cats(&chkpacket,connectingip)) die_nomem();
+  if (!stralloc_0(&chkpacket)) die_nomem();
+  if (!stralloc_append(&chkpacket, "F")) die_nomem();
+  if (!stralloc_cats(&chkpacket,from)) die_nomem();
+  if (!stralloc_0(&chkpacket)) die_nomem();
+  if (!stralloc_catb(&chkpacket,tolist, tolen)) die_nomem();
+
+/* For a long address list ignore tail; sender is very likely to repeat initial
+ * addresses on retry. Alternatively could be more rigorous by sending each
+ * recipient in a separate query if chkpacket.len > MAXGREYDATASIZE or calling
+ * greycheck() at RCPT time for each addr (saving result until DATA time).
+ */
+  if (chkpacket.len > MAXGREYDATASIZE) chkpacket.len = MAXGREYDATASIZE;
+
+  r = query_skt(sockfd, &chkpacket, rbuf, sizeof rbuf, GREYTIMEOUT, timeoutfn, errfn);
+
+  if(r > 0 && rbuf[0] == 0) return 0; /* greylist */
+
+  if(r == 0) return 1; /* Permit connection (soft fail) - probably timeout */
+
+  return 1;
+}
diff -Nru netqmail-1.06_errmsg/greydaemon.8 netqmail-1.06_errmsg_grey1.15/greydaemon.8
--- netqmail-1.06_errmsg/greydaemon.8	1970-01-01 01:00:00.000000000 +0100
+++ netqmail-1.06_errmsg_grey1.15/greydaemon.8	2009-07-20 19:09:23.000000000 +0100
@@ -0,0 +1,118 @@
+.TH greydaemon 8
+.SH NAME
+greydaemon \- greylisting daemon
+.SH SYNOPSIS
+.B greydaemon
+[\c
+.B \-u
+.I username\c
+]\ [\c
+.B \-w
+.I whitelist\c
+]\ [\c
+.B \-t
+.I timeout_days\c
+]\ [\c
+.B \-g
+.I resend_window_hours\c
+]\ [\c
+.B \-m
+.I min_resend_minutes\c
+]
+.I ipaddr savefile
+.SH DESCRIPTION
+.B greydaemon
+is a greylisting daemon responding to UDP query packets, typically
+sent by a modified
+.B qmail-smtpd.
+Queries consist of the sending IP address, the sender address and
+one or more recipient addresses. 
+
+If the IP address was previously successful for a greylisting check and
+was last queried within
+.I timeout_days
+the check succeeds. Alternatively if
+one of the supplied triplets of
+IP address / sender / recipient have previously been seen within
+.I resend_window_hours
+but at least
+.I min_resend_minutes
+ago, the check succeeds and future checks within
+.I resend_window_hours
+for the IP address will succeed.
+Otherwise the triplet(s) supplied are added to the greylisting database
+to check against future queries, and the check fails (meaning
+.B qmail-smtpd
+will reject the message).
+
+.B greydaemon
+must be started as root but quickly changes its effective
+user/group id to that specified by
+.I username.
+
+.B greydaemon
+maintains its database in memory, thus
+avoiding complicated schemes to manage greylisting data on disk -
+as well as benefitting from being faster than disk-based
+approaches.
+Nevertheless periodic backups of the database are made to disk to
+enable greydaemon to start with existing greylisting data if
+greydaemon restarts, such as when the machine is rebooted.
+
+.B greydaemon
+listens on IP address
+.I ipaddr\c
+, port 1999 for incoming UDP queries. 127.0.0.1 (the loopback address)
+is recommended for
+.I ipaddr
+if
+.B greydaemon
+is to serve queries on the same machine.
+
+At start-up the file
+.I savefile
+is read. This contains the list of currently  greylisted addresses;
+periodically (about every 10 minutes)
+.B greydaemon
+writes a new
+.I savefile\fR.
+Since
+.B greydaemon
+runs as
+.I username
+,
+.I savefile
+and its containing directory should be writeable by
+.I username.
+
+.SH OPTIONS
+.TP
+.B -u \fI username
+run as user
+.I username
+.TP
+.B -w \fI filename
+specify whitelist of IP ranges not subject to greylisting
+.TP
+.B -t \fIdays
+timeout for known IPs in days; defaults to 7.
+.TP
+.B -g \fIhours
+grey resend window, in hours; defaults to 12.
+.TP
+.B -m \fIminutes
+min resend accept time, in minutes; defaults to 5.
+
+.SH "QUERY FORMAT"
+Queries to greydaemon are UDP packets containing the IP address (as a string)
+preceded by I; the sender address preceded by F and the recipient address
+preceded by T. Each of these fields is separated by an ASCII 0 (null) character.
+Additional recipient addresses may be appended to this structure - each time
+preceded with T and with an ASCII 0 as separator.
+
+.SH "SEE ALSO"
+qmail-smtpd(8).
+
+.SH AUTHORS
+.B greydaemon
+is written by John Levine. This man page is written by Andrew Richards.
diff -Nru netqmail-1.06_errmsg/greydaemon.body netqmail-1.06_errmsg_grey1.15/greydaemon.body
--- netqmail-1.06_errmsg/greydaemon.body	1970-01-01 01:00:00.000000000 +0100
+++ netqmail-1.06_errmsg_grey1.15/greydaemon.body	2009-07-20 19:09:23.000000000 +0100
@@ -0,0 +1,282 @@
+# -*- perl -*-
+# $Header: /home/johnl/hack/RCS/greydaemon,v 1.8 2004/05/28 19:31:36 johnl Exp $
+
+# greydaemon [ flags ] ipaddr savefile
+
+# -u username run as username
+# -w whitelist of IP ranges
+# -t timeout for known IPs in days
+# -g grey resend window, in hours
+# -m min resend accept time, in minutes
+
+# Copyright (c) 2009, John R. Levine, Taughannock Networks
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+#  * Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+#
+#  * Redistributions in binary form must reproduce the above
+#    copyright notice, this list of conditions and the following
+#    disclaimer in the documentation and/or other materials provided
+#    with the distribution.
+#
+#  * Neither the name of Taughannock Networks nor the names of its
+#    contributors may be used to endorse or promote products derived
+#    from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+require 5.002;
+use strict;
+use IO::Socket;
+use Getopt::Std;
+
+sub newranges();
+sub addrange( $ );
+sub endranges();
+sub checkrange( $ );
+
+use vars qw($opt_d $opt_g $opt_m $opt_t $opt_u $opt_w %ips %grey);
+getopts("dg:m:t:u:w:");
+
+$| = 1;
+
+my ($ipaddr, $port, $savefile, $server, $rmt, $msg, $omsg, $now);
+
+$ipaddr = shift or die "Need IP address to serve on";
+
+$savefile = shift or die "Need name of context save file";
+my $savetime = 600;		# how long between saves
+my $lastsave;
+
+my $timeout = ($opt_t || 7)*3600*24;
+my $maxgrey = ($opt_g || 12)*3600;
+my $mingrey = ($opt_m || 5)*60;
+
+
+if($ipaddr =~ /(.*):(.*)/) {
+    $ipaddr = $1;
+    $port = $2;
+} else {
+    $port = 1999;
+}
+
+$server = IO::Socket::INET->new(LocalAddr => $ipaddr,
+				LocalPort => $port,
+				Proto => "udp")
+    or die "socket failed $ipaddr:$port $!";
+
+if(defined($opt_u)) {
+    my ($name, $pwd, $uid, $gid) = getpwnam $opt_u or die "can't find $opt_u";
+    $( = $) = $gid;
+    $< = $> = $uid;
+    print "Now running as user $opt_u ($uid, $gid)\n";
+}
+
+if($opt_w) {
+    open(WHITE, "<$opt_w") or die "? cannot open $opt_w";
+
+    newranges();
+    
+    while(<WHITE>) {
+	s/#.*//;
+	next if /^\s*$/;
+	chomp;
+	addrange($_);
+    }
+    close WHITE;
+    endranges();
+}
+
+$lastsave = $now = time;
+
+if(open(SAVE, "<$savefile")) {
+    my $tl = $now - $timeout;
+    my $gtl = $now - $maxgrey;
+    
+    print "load $savefile\n" if $opt_d;
+
+    while(<SAVE>) {
+	if(/^I (\d+) ([0-9.]+)/) {
+	    print "  ip $1 = $2\n" if $opt_d and $1 > $tl;
+	    $ips{$2} = $1 if $1 > $tl;
+	} elsif(/^G (\d+) ([0-9.]+) (\S+) (\S+)/) {
+	    $grey{"$2 $3 $4"} = $1 if $1 > $gtl;
+	    print "  grey $1 = $2 $3 $4\n" if $opt_d and $1 > $gtl;
+	} else {
+	    print "? Strange save entry $_";
+	}
+    }
+    close SAVE;
+}
+
+print "start greydaemon on $ipaddr:$port\n";
+
+mainloop: while($rmt = $server->recv($msg, 2048)) {       # 2048 = MAXGREYPKTSIZE in grey.h
+    my ($rport, $raddr) = sockaddr_in($server->peername);
+    my ($addr, $resp, $ip, $from, $to, @args);
+
+    $addr = inet_ntoa($raddr);
+    $now = time;
+
+    my $dmsg = $msg;
+    $dmsg =~ s/\0/ /g;
+
+    @args = split /\0/,$msg;
+
+    if((shift @args) =~ m{I(.*)}) {
+	$ip = $1;
+    } else {
+	print "$addr:$rport bad req no I $dmsg\n";
+	next;
+    }
+    
+    if(checkrange($ip)) {
+	print "$addr:$rport white $dmsg\n";
+	$resp = "\1\1";
+    } elsif(defined $ips{$ip} && $ips{$ip} > ($now - $timeout)) {
+	$ips{$ip} = $now;
+	print "$addr:$rport ok ip $dmsg\n";
+	$resp = "\1\2";
+    } else {
+	if((shift @args) =~ /F(.*)/) {
+	    $from = $1;
+	    $from =~ s/ //g;	# no spaces allowed
+	    $from = "." if $from eq "";
+	} else {
+	    print "$addr:$rport bad req no F $dmsg\n";
+	    next mainloop;
+	}
+	$resp = "\1\3";
+	for $to (@args) {
+	    unless($to =~ s/^T//) {
+		print "$addr:$rport bad req no T $dmsg\n";
+		next mainloop;
+	    }
+	    $to =~ s/ //g;	# no spaces allowed
+	    if($from eq "." and $to =~ /-.*=/) {
+		$resp = "\1\4";	# bounce
+		print "$addr:$rport bounce $dmsg\n";
+		last;
+	    }
+	    my $gt = $grey{lc "$ip $from $to"};
+	    if($gt) {
+		if(($now - $gt) < $mingrey) {
+		    $resp = "\0\2"; # too new
+		    print "$addr:$rport retry too soon $dmsg\n";
+		} elsif(($now - $gt) < $maxgrey) {
+		    $ips{$ip} = $now;
+		    print "$addr:$rport retry ok $dmsg\n";
+		} else {
+		    $grey{lc "$ip $from $to"} = $now;
+		    $resp = "\0\3";
+		    print "$addr:$rport stale grey $dmsg\n";
+		}
+	    } else {
+		$grey{lc "$ip $from $to"} = $now;
+		$resp = "\0\1";
+		print "$addr:$rport new grey $dmsg\n";
+	    }
+	}
+    }
+
+    $server->send($resp);
+
+    if($opt_d or ($now - $lastsave) > $savetime) {
+	my $tl = $now - $timeout;
+	my $gtl = $now - $maxgrey;
+	my ($t, $k);
+
+	print "save status to $savefile\n";
+
+	open(SAVE, ">$savefile.new") or die "create $savefile.new";
+
+	while(($ip, $t) = each %ips) {
+	    print SAVE "I $t $ip\n" if $t > $tl;
+	}
+	while(($k, $t) = each %grey) {
+	    print SAVE "G $t $k\n" if $t > $gtl;
+	}
+	close SAVE;
+	unlink $savefile;
+	rename "$savefile.new",$savefile;
+	$lastsave = $now;
+    }
+	
+# main loop
+}
+
+################################################################
+# a testable set of IP ranges
+my (%ranges, @masks, %masks);
+
+sub newranges() {
+    %ranges = ();
+    %masks = @masks = ();
+}
+
+sub addrange($) {
+    my ($range) = @_;
+    my ($addr, $mask);
+
+    print "addrange $range " if $opt_d;
+
+    if($range =~ m{(\d[0-9.]+)/(\d+)}) {
+	$addr = $1; $mask = $2;
+    } elsif($range =~ m{(\d+\.\d+\.\d+\.\d+)}) {
+	$addr = $1; $mask = 32;
+    } elsif($range =~ m{(\d+\.\d+\.\d+)}) {
+	$addr = $1; $mask = 24;
+    } elsif($range =~ m{(\d+\.\d+)}) {
+	$addr = $1; $mask = 16;
+    } elsif($range =~ m{(\d+)}) {
+	$addr = $1; $mask = 8;
+    } else { 
+	die "? bad range $range";
+    }
+
+    my $naddr = unpack "N", pack "CCCC", split /\./, $addr;
+
+    printf " = %08x / %d\n", $naddr, $mask if $opt_d;
+
+    $ranges{$naddr} = $mask;
+    $masks{$mask} = 1;
+}
+
+sub endranges() {
+
+    @masks = sort { $b <=> $a } keys %masks; # largest to smallest mask
+
+    print "masks " . join(" ", @masks) . "\n" if $opt_d;
+
+}
+
+sub checkrange($) {
+    my ($range) = @_;
+    my ($i);
+    my $nrange = unpack "N", pack "CCCC", split /\./, $range;
+
+    foreach $i (@masks) {
+	my $m = (2**32) - 2**(32-$i);
+
+	my $mv = $nrange & $m;
+
+	return 1 if defined($ranges{$mv}) and $ranges{$mv} <= $i;
+    }
+    0;
+}
diff -Nru netqmail-1.06_errmsg/grey.h netqmail-1.06_errmsg_grey1.15/grey.h
--- netqmail-1.06_errmsg/grey.h	1970-01-01 01:00:00.000000000 +0100
+++ netqmail-1.06_errmsg_grey1.15/grey.h	2009-07-20 19:09:23.000000000 +0100
@@ -0,0 +1,11 @@
+#ifndef GREY_H
+#define GREY_H
+
+#define MAXGREYDATASIZE 2000
+#define DEFAULTGREYPORT 1999
+#define DEFAULTGREYIP "127.0.0.1"
+#define GREYTIMEOUT 3
+
+extern int greycheck();
+
+#endif
diff -Nru netqmail-1.06_errmsg/hier.c netqmail-1.06_errmsg_grey1.15/hier.c
--- netqmail-1.06_errmsg/hier.c	1998-06-15 11:53:16.000000000 +0100
+++ netqmail-1.06_errmsg_grey1.15/hier.c	2009-07-20 19:09:23.000000000 +0100
@@ -143,6 +143,7 @@
   c(auto_qmail,"bin","qail",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","elq",auto_uido,auto_gidq,0755);
   c(auto_qmail,"bin","pinq",auto_uido,auto_gidq,0755);
+  c(auto_qmail,"bin","greydaemon",auto_uido,auto_gidq,0755);
 
   c(auto_qmail,"man/man5","addresses.5",auto_uido,auto_gidq,0644);
   c(auto_qmail,"man/cat5","addresses.0",auto_uido,auto_gidq,0644);
@@ -249,4 +250,6 @@
   c(auto_qmail,"man/cat8","qmail-smtpd.0",auto_uido,auto_gidq,0644);
   c(auto_qmail,"man/man8","qmail-command.8",auto_uido,auto_gidq,0644);
   c(auto_qmail,"man/cat8","qmail-command.0",auto_uido,auto_gidq,0644);
+  c(auto_qmail,"man/man8","greydaemon.8",auto_uido,auto_gidq,0644);
+  c(auto_qmail,"man/cat8","greydaemon.0",auto_uido,auto_gidq,0644);
 }
diff -Nru netqmail-1.06_errmsg/Makefile netqmail-1.06_errmsg_grey1.15/Makefile
--- netqmail-1.06_errmsg/Makefile	2009-04-28 16:26:19.000000000 +0100
+++ netqmail-1.06_errmsg_grey1.15/Makefile	2009-07-20 19:09:23.000000000 +0100
@@ -637,6 +637,20 @@
 compile gfrom.c str.h gfrom.h
 	./compile gfrom.c
 
+grey.o: \
+compile grey.c grey.h
+	./compile grey.c
+
+greydaemon: \
+greydaemon.body
+	which perl > /dev/null
+	echo "#!`which perl`" > greydaemon
+	cat greydaemon.body >> greydaemon
+
+greydaemon.0: \
+greydaemon.8
+	nroff -man greydaemon.8 > greydaemon.0
+
 hasflock.h: \
 tryflock.c compile load
 	( ( ./compile tryflock.c && ./load tryflock ) >/dev/null \
@@ -812,7 +826,7 @@
 forward preline condredirect bouncesaying except maildirmake \
 maildir2mbox maildirwatch qail elq pinq idedit install-big install \
 instcheck home home+df proc proc+df binm1 binm1+df binm2 binm2+df \
-binm3 binm3+df
+binm3 binm3+df greydaemon
 
 load: \
 make-load warn-auto.sh systype
@@ -939,7 +953,7 @@
 maildir2mbox.0 maildirwatch.0 qmail.0 qmail-limits.0 qmail-log.0 \
 qmail-control.0 qmail-header.0 qmail-users.0 dot-qmail.0 \
 qmail-command.0 tcp-environ.0 maildir.0 mbox.0 addresses.0 \
-envelopes.0 forgeries.0
+envelopes.0 forgeries.0 greydaemon.0
 
 mbox.0: \
 mbox.5
@@ -1537,14 +1551,19 @@
 
 qmail-smtpd: \
 load qmail-smtpd.o rcpthosts.o commands.o timeoutread.o \
+sockbits.o udpbits.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 \
+date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
+scan_misc.o grey.o ndelay.a \
+datetime.a getln.a \
 open.a sig.a case.a env.a stralloc.a errbits.o \
 alloc.a substdio.a error.a str.a \
 fs.a auto_qmail.o socket.lib
 	./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \
+	sockbits.o udpbits.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 \
+	scan_misc.o grey.o ndelay.a \
 	datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
 	errbits.o \
 	alloc.a substdio.a error.a str.a fs.a auto_qmail.o  `cat \
@@ -1559,6 +1578,7 @@
 substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \
 error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \
 substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \
+sockbits.h udpbits.h \
 exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h
 	./compile qmail-smtpd.c
 
@@ -1706,6 +1726,10 @@
 compile scan_8long.c scan.h
 	./compile scan_8long.c
 
+scan_misc.o: \
+compile scan_misc.c scan.h
+	./compile scan_misc.c
+
 scan_ulong.o: \
 compile scan_ulong.c scan.h
 	./compile scan_ulong.c
@@ -1768,6 +1792,7 @@
 BIN.Makefile BIN.setup idedit.c conf-break auto_break.h conf-spawn \
 auto_spawn.h chkspawn.c conf-split auto_split.h conf-patrn \
 auto_patrn.h conf-users conf-groups auto_uids.h auto_usera.h extra.h \
+greydaemon.body greydaemon.8 \
 addresses.5 except.1 bouncesaying.1 condredirect.1 dot-qmail.9 \
 envelopes.5 forgeries.7 forward.1 maildir2mbox.1 maildirmake.1 \
 maildirwatch.1 mailsubj.1 mbox.5 preline.1 qbiff.1 qmail-clean.8 \
@@ -1889,6 +1914,10 @@
 error.h
 	./compile slurpclose.c
 
+sockbits.o: \
+compile sockbits.c sockbits.h stralloc.h gen_alloc.h
+	./compile sockbits.c
+
 socket.lib: \
 trylsock.c compile load
 	( ( ./compile trylsock.c && \
@@ -2127,6 +2156,10 @@
 compile triggerpull.c ndelay.h open.h triggerpull.h
 	./compile triggerpull.c
 
+udpbits.o: \
+compile udpbits.c udpbits.h ip.h
+	./compile udpbits.c
+
 uint32.h: \
 tryulong32.c compile load uint32.h1 uint32.h2
 	( ( ./compile tryulong32.c && ./load tryulong32 && \
diff -Nru netqmail-1.06_errmsg/qmail-smtpd.8 netqmail-1.06_errmsg_grey1.15/qmail-smtpd.8
--- netqmail-1.06_errmsg/qmail-smtpd.8	1998-06-15 11:53:16.000000000 +0100
+++ netqmail-1.06_errmsg_grey1.15/qmail-smtpd.8	2009-07-20 19:09:23.000000000 +0100
@@ -169,6 +169,49 @@
 .B qmail-smtpd
 will wait for each new buffer of data from the remote SMTP client.
 Default: 1200.
+.SH GREYLISTING
+To enable greylisting the environment variable
+.B GREYIP
+is used. For the default values (loopback address 127.0.0.1, port 1999) use,
+
+.EX
+   GREYIP=":"
+.EE
+
+Alternatively a different IP address and/or port can be specified as
+in these examples,
+
+.EX
+   GREYIP="192.168.1.50"
+   GREYIP=":12345"
+   GREYIP="192.168.1.33:54321"
+.EE
+
+Exception:
+If the environment variable
+.B RELAYCLIENT
+is set (see details on this above), greylisting does not occur.
+
+Greylisting may be explicitly disabled by setting
+.B GREYIP
+to an empty string,
+
+.EX
+   GREYIP=""
+.EE
+
+Clearly
+.B greydaemon
+must be available to respond to greylisting queries. If the
+GREYIP address/port is unavailable and the system detects
+this - perhaps via an ICMP 'unreachable' type response - the
+SMTP session aborts with a temporary error when the
+greylisting check occurs; the errno string is logged to help
+troubleshooting (typically greydaemon isn't running or is
+blocked by firewall rules). Otherwise if there is no response
+a timeout occurs, producing the same temporary error but without
+further logging detail.
+
 .SH "SEE ALSO"
 tcp-env(1),
 tcp-environ(5),
@@ -176,4 +219,5 @@
 qmail-inject(8),
 qmail-newmrh(8),
 qmail-queue(8),
-qmail-remote(8)
+qmail-remote(8),
+greydaemon(8)
diff -Nru netqmail-1.06_errmsg/qmail-smtpd.c netqmail-1.06_errmsg_grey1.15/qmail-smtpd.c
--- netqmail-1.06_errmsg/qmail-smtpd.c	2009-04-28 16:26:19.000000000 +0100
+++ netqmail-1.06_errmsg_grey1.15/qmail-smtpd.c	2009-07-20 19:09:23.000000000 +0100
@@ -24,6 +24,7 @@
 #include "timeoutwrite.h"
 #include "commands.h"
 #include "errbits.h"
+#include "grey.h"
 
 #define enew()	{ eout("qmail-smtpd: pid "); epid(); eout3(" from ",remoteip,": "); }
 /* Or if you prefer shorter log messages (deduce IP from tcpserver PID entry), */
@@ -37,6 +38,7 @@
 char *remoteinfo;
 char *local;
 char *relayclient;
+char *greyip;
 
 stralloc mailfrom = {0};
 stralloc rcptto = {0};
@@ -148,6 +150,24 @@
   enew(); eout("Exceeded DATABYTES limit\n"); eflush();
   out("552 sorry, that message size exceeds my databytes limit (#5.3.4)\r\n");
 }
+void err_greytimeout()
+{
+  enew(); eout("Timeout (no response from greylisting server)\n"); eflush();
+  out("451 gd temporary failure (#4.3.0)\r\n"); flush(); _exit(1);
+}
+void err_greymiscfail()
+{
+  enew(); eout3("greylisting failure (",error_str(errno),"): quitting\n"); eflush();
+  out("451 gd temporary failure (#4.3.0)\r\n"); flush(); _exit(1);
+}
+void err_grey()
+{
+  enew(); eout7("greylist ",remoteip," <",mailfrom.s,"> to <",rcptto.s+1,">");
+  if (rcptcount > 1) eout("..."); /* >1 address sent for greylist check */
+  eout("\n");
+  eflush();
+  out("450 try again later (#4.3.0)\r\n");
+}
 
 
 stralloc greeting = {0};
@@ -216,6 +236,8 @@
   if (!remotehost) remotehost = "unknown";
   remoteinfo = env_get("TCPREMOTEINFO");
   relayclient = env_get("RELAYCLIENT");
+  greyip = env_get("GREYIP");
+  if ((greyip) && (*greyip == '\0')) greyip = (char *)0; /* Disable greylisting if GREYIP="" */
   dohelo(remotehost);
   enew(); eout("New session\n"); eflush();
 }
@@ -468,6 +490,10 @@
  
   if (!seenmail) { err_wantmail(); return; }
   if (!rcptto.len) { err_wantrcpt(); return; }
+  if (greyip && !relayclient)
+  {
+    if (!greycheck(greyip, remoteip, mailfrom.s, rcptto.s, rcptto.len, err_greytimeout, err_greymiscfail)) { err_grey(); return; }
+  }
   seenmail = 0;
   if (databytes) bytestooverflow = databytes + 1;
   messagebytes = 0;
diff -Nru netqmail-1.06_errmsg/scan.h netqmail-1.06_errmsg_grey1.15/scan.h
--- netqmail-1.06_errmsg/scan.h	1998-06-15 11:53:16.000000000 +0100
+++ netqmail-1.06_errmsg_grey1.15/scan.h	2009-07-20 19:09:23.000000000 +0100
@@ -24,4 +24,7 @@
 
 extern unsigned int scan_long();
 
+extern char *find_digit_colon_eos();
+extern int scan_ip_port();
+
 #endif
diff -Nru netqmail-1.06_errmsg/scan_misc.c netqmail-1.06_errmsg_grey1.15/scan_misc.c
--- netqmail-1.06_errmsg/scan_misc.c	1970-01-01 01:00:00.000000000 +0100
+++ netqmail-1.06_errmsg_grey1.15/scan_misc.c	2009-08-24 14:53:42.000000000 +0100
@@ -0,0 +1,39 @@
+#include "stralloc.h"
+#include "ip.h"
+#include "scan.h"
+
+/* Returns pointer to first digit or ':' in string, or end-of-string
+ * if neither found. Useful prior to scan_ip_port() if any options
+ * may precede the IP / port in the string. */
+char *find_digit_colon_eos(s)
+char *s;
+{
+  while (*s != '\0')
+  {
+    if ( *s == ':') return s;
+    if ((*s >= '0') && (*s <= '9')) return s;
+    s++;
+  }
+  return s; /* end of string '\0' */
+}
+
+/* Takes a string specifying IP address and port, separated by ':'
+ * If IP address and/or port are missing, supplied defaults are used.
+ * 0, -1 returned on success, failure respectively. */
+int scan_ip_port(s,defaultip,defaultport,ipp,portp)
+char *s, *defaultip;
+struct ip_address *ipp;
+unsigned int defaultport, *portp;
+{
+  int n;
+  char *sp;
+  unsigned long port; /* long because of scan_ulong */
+
+  if (!s) return -1; /* Can't scan a null string */
+  sp = s;
+  if (!(n=ip_scan(sp, ipp))) ip_scan(defaultip,ipp);
+  sp += n; /* n is 0 if no IP found */
+  if (!((*sp==':') && scan_ulong(sp+1,&port))) port=defaultport;
+  *portp = (unsigned int)port;
+  return 0;
+}
diff -Nru netqmail-1.06_errmsg/sockbits.c netqmail-1.06_errmsg_grey1.15/sockbits.c
--- netqmail-1.06_errmsg/sockbits.c	1970-01-01 01:00:00.000000000 +0100
+++ netqmail-1.06_errmsg_grey1.15/sockbits.c	2009-07-20 19:09:23.000000000 +0100
@@ -0,0 +1,31 @@
+#include "sockbits.h"
+#include "stralloc.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include "error.h"
+
+int query_skt(fd,queryp,responsep,maxresponsesize,timeout,timeoutfn,errfn)
+int fd;
+stralloc *queryp;
+char *responsep;
+int maxresponsesize, timeout;
+void (*errfn)(), (*timeoutfn)();
+{
+  fd_set rfs;
+  struct timeval tv;
+  int nbytes;
+  int r=0;
+
+  if (write(fd,queryp->s,queryp->len) < 0) (*errfn)();
+  tv.tv_sec=timeout; tv.tv_usec=0;
+  FD_ZERO(&rfs); FD_SET(fd,&rfs);
+  if ((r=select(fd+1,&rfs,(fd_set *) 0,(fd_set *) 0,&tv)) <= 0) /* 0 timeout or -1 error */
+  {
+    if ((r == 0) && (errno == error_timeout)) (*timeoutfn)();
+    else (*errfn)();
+    return r; /* if timeoutfn() / errfn() doesn't _exit() */
+  }
+  nbytes = read(fd,responsep,maxresponsesize);
+  if (nbytes < 0) (*errfn)();
+  return (nbytes); /* including 0 = no output */
+}
diff -Nru netqmail-1.06_errmsg/sockbits.h netqmail-1.06_errmsg_grey1.15/sockbits.h
--- netqmail-1.06_errmsg/sockbits.h	1970-01-01 01:00:00.000000000 +0100
+++ netqmail-1.06_errmsg_grey1.15/sockbits.h	2009-07-20 19:09:23.000000000 +0100
@@ -0,0 +1 @@
+extern int query_skt();
diff -Nru netqmail-1.06_errmsg/TARGETS netqmail-1.06_errmsg_grey1.15/TARGETS
--- netqmail-1.06_errmsg/TARGETS	2009-04-28 16:26:19.000000000 +0100
+++ netqmail-1.06_errmsg_grey1.15/TARGETS	2009-07-20 19:09:23.000000000 +0100
@@ -251,6 +251,12 @@
 qmail-qmtpd.o
 rcpthosts.o
 qmail-qmtpd
+sockbits.o
+udpbits.o
+scan_misc.o
+grey.o
+greydaemon
+greydaemon.0
 qmail-smtpd.o
 qmail-smtpd
 sendmail.o
diff -Nru netqmail-1.06_errmsg/udpbits.c netqmail-1.06_errmsg_grey1.15/udpbits.c
--- netqmail-1.06_errmsg/udpbits.c	1970-01-01 01:00:00.000000000 +0100
+++ netqmail-1.06_errmsg_grey1.15/udpbits.c	2009-07-20 19:09:23.000000000 +0100
@@ -0,0 +1,24 @@
+#include "udpbits.h"
+#include "ip.h"
+#include "byte.h"
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+int connect_udp(ip,port,errfn)
+struct ip_address ip;
+unsigned int port;
+void (*errfn)();
+{
+  struct sockaddr_in sout;
+  int fd;
+
+  byte_zero(&sout,sizeof(sout));
+  sout.sin_port = htons(port);
+  sout.sin_family=AF_INET;
+  byte_copy(&sout.sin_addr,sizeof(ip),&ip);
+/*sout.sin_len = sizeof(sout); Commented out since optional & sin_len not defined on all OSes */
+  if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) (*errfn)();
+  if (connect(fd,(struct sockaddr *)&sout,sizeof(sout)) < 0) (*errfn)();
+  return fd;
+}
diff -Nru netqmail-1.06_errmsg/udpbits.h netqmail-1.06_errmsg_grey1.15/udpbits.h
--- netqmail-1.06_errmsg/udpbits.h	1970-01-01 01:00:00.000000000 +0100
+++ netqmail-1.06_errmsg_grey1.15/udpbits.h	2009-07-20 19:09:23.000000000 +0100
@@ -0,0 +1 @@
+extern int connect_udp();
