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
> 

Reply via email to