This doesn't apply cleanly, because it comes after your perltidy CL. (i.e. it has whitespace changes that break the patch.)
Matt Simerson wrote: > > added p0f config option > added POD docs to explain usage > modified $dbdir selection logic. The previous logic failed when QPHOME was > not selected (as is the case when tests are being run). > Added '.' as the dir of last resort for $dbdir selection (others $EMPTY/dir > dumped greylisting database in / ) > Added t/plugin_tests/greylisting, with greylist logic testing (tests are > disabled by default, as greylisting is disabled in config.sample/plugins) > --- > plugins/greylisting | 57 +++++++++++++++++++++-- > t/plugin_tests/greylisting | 109 > ++++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 161 insertions(+), 5 deletions(-) > create mode 100644 t/plugin_tests/greylisting > > diff --git a/plugins/greylisting b/plugins/greylisting > index 2bdfbc1..9d28ce5 100644 > --- a/plugins/greylisting > +++ b/plugins/greylisting > @@ -105,6 +105,23 @@ Flag to indicate whether to use per-recipient greylisting > databases (default is to use a shared database). Per-recipient configuration > directories, if determined, supercede I<db_dir>. > > +=item p0f > + > +Enable greylisting only when certain p0f criteria is met. The single > +required argument is a comma delimited list of key/value pairs. The keys > +are the following p0f TCP fingerprint elements: genre, detail, uptime, > +link, and distance. > + > +To greylist emails from computers whose remote OS is windows, you'd use > +this syntax: > + > + p0f genre,windows > + > +To greylist only windows computers on DSL links more than 3 network hops > +away: > + > + p0f genre,windows,link,dsl,distance,3 > + > =back > > =head1 BUGS > @@ -117,6 +134,8 @@ use something like File::NFSLock instead. > > Written by Gavin Carr <ga...@openfusion.com.au>. > > +Added p0f section <mattsimer...@cpan.org> (2010-05-03) > + > =cut > > BEGIN { @AnyDBM_File::ISA = qw(DB_File GDBM_File NDBM_File) } > @@ -124,13 +143,13 @@ use AnyDBM_File; > use Fcntl qw(:DEFAULT :flock); > use strict; > > -my $VERSION = '0.07'; > +my $VERSION = '0.08'; > > my $DENYMSG = "This mail is temporarily denied"; > my ($QPHOME) = ($0 =~ m!(.*?)/([^/]+)$!); > my $DB = "denysoft_greylist.dbm"; > my %PERMITTED_ARGS = map { $_ => 1 } qw(per_recipient remote_ip sender > recipient > - black_timeout grey_timeout white_timeout deny_late mode db_dir); > + black_timeout grey_timeout white_timeout deny_late mode db_dir p0f ); > > my %DEFAULTS = ( > remote_ip => 1, > @@ -140,6 +159,7 @@ my %DEFAULTS = ( > grey_timeout => 3 * 3600 + 20 * 60, > white_timeout => 36 * 24 * 3600, > mode => 'denysoft', > + p0f => undef, > ); > > sub register { > @@ -224,17 +244,23 @@ sub denysoft_greylist { > return DECLINED if $self->qp->connection->notes('whitelisthost'); > return DECLINED if $transaction->notes('whitelistsender'); > > + # do not greylist if p0f matching is selected and message does not match > + return DECLINED if $config->{'p0f'} && !$self->p0f_match( $config ); > + > if ($config->{db_dir} && $config->{db_dir} =~ m{^([-a-zA-Z0-9./_]+)$}) { > $config->{db_dir} = $1; > } > > # Setup database location > - my $dbdir = $transaction->notes('per_rcpt_configdir') > + my $dbdir; > + $dbdir = $transaction->notes('per_rcpt_configdir') > if $config->{per_recipient_db}; > for my $d ($dbdir, $config->{db_dir}, "/var/lib/qpsmtpd/greylisting", > - "$QPHOME/var/db", "$QPHOME/config") > + "$QPHOME/var/db", "$QPHOME/config", '.' ) > { > - last if $dbdir ||= $d && -d $d && $d; > + last if $dbdir && -d $dbdir; > + next if ( ! $d || ! -d $d ); > + $dbdir = $d; > } > my $db = "$dbdir/$DB"; > $self->log(LOGINFO, "using $db as greylisting database"); > @@ -316,5 +342,26 @@ sub denysoft_greylist { > return $config->{mode} eq 'testonly' ? DECLINED : DENYSOFT, $DENYMSG; > } > > +sub p0f_match { > + my $self = shift; > + my $config = shift; > + > + my $p0f = $self->connection->notes('p0f'); > + return if !$p0f || !ref $p0f; # p0f fingerprint info not found > + > + my %valid_matches = map { $_ => 1 } qw( genre detail uptime link > distance ); > + my %requested_matches = split(/\,/, $config->{'p0f'} ); > + > + foreach my $key (keys %requested_matches) { > + next if !defined $valid_matches{$key}; # discard invalid match keys > + my $value = $requested_matches{$key}; > + return 1 if $key eq 'distance' && $p0f->{$key} > $value; > + return 1 if $key eq 'genre' && $p0f->{$key} =~ /$value/i; > + return 1 if $key eq 'uptime' && $p0f->{$key} < $value; > + return 1 if $key eq 'link' && $p0f->{$key} =~ /$value/i; > + } > + return; > +} > + > # arch-tag: 6ef5919e-404b-4c87-bcfe-7e9f383f3901 > > diff --git a/t/plugin_tests/greylisting b/t/plugin_tests/greylisting > new file mode 100644 > index 0000000..5c8f068 > --- /dev/null > +++ b/t/plugin_tests/greylisting > @@ -0,0 +1,109 @@ > +use Qpsmtpd::Address; > + > +my $test_email = 'u...@example.com'; > +my $address = Qpsmtpd::Address->new( "<$test_email>" ); > + > +my @greydbs = qw( denysoft_greylist.dbm denysoft_greylist.dbm.lock ); > +foreach ( @greydbs ) { > + unlink $_ if -f $_; > +}; > + > +sub register_tests { > + my $self = shift; > + $self->register_test("test_greylist_p0f_genre_miss", 1); > + $self->register_test("test_greylist_p0f_genre_hit", 1); > + $self->register_test("test_greylist_p0f_distance_hit", 1); > + $self->register_test("test_greylist_p0f_distance_miss", 1); > + $self->register_test("test_greylist_p0f_link_hit", 1); > + $self->register_test("test_greylist_p0f_link_miss", 1); > + $self->register_test("test_greylist_p0f_uptime_hit", 1); > + $self->register_test("test_greylist_p0f_uptime_miss", 1); > +} > + > +sub test_greylist_p0f_genre_miss { > + my $self = shift; > + > + $self->{_greylist_config}{'p0f'} = 'genre,Linux'; > + $self->connection->notes('p0f'=> { genre => 'windows', link => 'dsl' } ); > + my $r = $self->rcpt_handler( $self->qp->transaction ); > + > + ok( $r == 909, 'p0f genre miss'); > +} > + > +sub test_greylist_p0f_genre_hit { > + my $self = shift; > + > + $self->{_greylist_config}{'p0f'} = 'genre,Windows'; > + $self->connection->notes('p0f'=> { genre => 'windows', link => 'dsl' } ); > + $self->qp->transaction->sender( $address ); > + my $r = $self->rcpt_handler( $self->qp->transaction ); > + > + ok( $r eq 'This mail is temporarily denied', 'p0f genre hit'); > +} > + > +sub test_greylist_p0f_distance_hit { > + my $self = shift; > + > + $self->{_greylist_config}{'p0f'} = 'distance,8'; > + $self->connection->notes('p0f'=> { distance=>9 } ); > + $self->qp->transaction->sender( $address ); > + my $r = $self->rcpt_handler( $self->qp->transaction ); > + > + ok( $r eq 'This mail is temporarily denied', 'p0f distance hit'); > +} > + > +sub test_greylist_p0f_distance_miss { > + my $self = shift; > + > + $self->{_greylist_config}{'p0f'} = 'distance,8'; > + $self->connection->notes('p0f'=> { distance=>7 } ); > + $self->qp->transaction->sender( $address ); > + my $r = $self->rcpt_handler( $self->qp->transaction ); > + > + ok( $r == 909, 'p0f distance miss'); > +} > + > +sub test_greylist_p0f_link_hit { > + my $self = shift; > + > + $self->{_greylist_config}{'p0f'} = 'link,dsl'; > + $self->connection->notes('p0f'=> { link=>'DSL' } ); > + $self->qp->transaction->sender( $address ); > + my $r = $self->rcpt_handler( $self->qp->transaction ); > + > + ok( $r eq 'This mail is temporarily denied', 'p0f link hit'); > +} > + > +sub test_greylist_p0f_link_miss { > + my $self = shift; > + > + $self->{_greylist_config}{'p0f'} = 'link,dsl'; > + $self->connection->notes('p0f'=> { link=>'Ethernet' } ); > + $self->qp->transaction->sender( $address ); > + my $r = $self->rcpt_handler( $self->qp->transaction ); > + > + ok( $r == 909, 'p0f link miss'); > +} > + > +sub test_greylist_p0f_uptime_hit { > + my $self = shift; > + > + $self->{_greylist_config}{'p0f'} = 'uptime,100'; > + $self->connection->notes('p0f'=> { uptime=> 99 } ); > + $self->qp->transaction->sender( $address ); > + my $r = $self->rcpt_handler( $self->qp->transaction ); > + > + ok( $r eq 'This mail is temporarily denied', 'p0f uptime hit'); > +} > + > +sub test_greylist_p0f_uptime_miss { > + my $self = shift; > + > + $self->{_greylist_config}{'p0f'} = 'uptime,100'; > + $self->connection->notes('p0f'=> { uptime=>500 } ); > + $self->qp->transaction->sender( $address ); > + my $r = $self->rcpt_handler( $self->qp->transaction ); > + > + ok( $r == 909, 'p0f uptime miss'); > +} > + > -- > 1.7.0.6 >