John Levine's greydaemon https://free.acrconsulting.co.uk/email/grey.html 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 +#include +#include +#include +#include +#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() { + 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() { + 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 +#include +#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 +#include +#include + +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();