Edit File: eximstats_spam_check
#!/usr/local/cpanel/3rdparty/bin/perl # cpanel - scripts/eximstats_spam_check Copyright 2018 cPanel, Inc. # All rights Reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited package scripts::eximstats_spam_check; use strict; use warnings; our $VERSION = 1.6; our $EXIMSTATS_SQLITE_DB = '/var/cpanel/eximstats_db.sqlite3'; use DBD::SQLite (); use Cpanel::Logger (); my $logger = Cpanel::Logger->new(); # MAXIMUM_RUN_TIME - sets the maximum duration in seconds before # gunning the query and putting an alert in the system # error log # SPAM_TIME - sets the span in seconds that we care about--how # recently must the messages must have been sent to be # considered spam # SPAM_COUNT - sets the minimum number of unique recipients for a sender # to be considered a spammer my $MAXIMUM_RUN_TIME = 3 * 60; my $SPAM_TIME = 60 * 60; my $SPAM_COUNT = 500; =head1 NAME scripts::eximstats_spam_check =head1 SYNOPSIS /usr/local/cpanel/scripts/eximstats_spam_check =head1 DESCRIPTION This script is used to query the eximstats database, and report possible spammers. =cut __PACKAGE__->new(@ARGV)->run() if !caller(); sub new { my $pkg = shift; my $self = {}; return bless $self, $pkg; } sub run { my ($self) = @_; local $@; my $ok = eval { $self->run_query() }; exit 0 if ($ok); exit 1; } sub run_query { my ($self) = @_; local $@; my $dbh = eval { DBI->connect( 'dbi:SQLite:' . $EXIMSTATS_SQLITE_DB, undef, undef, { sqlite_open_flags => DBD::SQLite::OPEN_READONLY(), sqlite_use_immediate_transaction => 0, RaiseError => 1, PrintWarn => 0, } ); }; if ( not $dbh or $DBI::errstr ) { my $err = $DBI::errstr // q{something went wrong}; my $msg = qq{'$EXIMSTATS_SQLITE_DB' - $err}; $logger->warn($msg); die qq{$msg\n}; } my $ts = time(); my $query = q{SELECT sender, count(sender) FROM ( SELECT DISTINCT sends.sender as sender, smtp.email as recipient FROM sends INNER JOIN smtp on (sends.msgid=smtp.msgid) WHERE sends.sender != '' AND sends.sendunixtime <= ? AND sends.sendunixtime > ? - ? AND smtp.transport_is_remote=1 AND smtp.transport_method != '**bypassed**' AND SUBSTR(smtp.transport_method,1,9) != 'archiver_' ) GROUP BY sender ORDER BY count(sender) DESC; }; my $results = $self->_get_results( $dbh, $query, $ts, $SPAM_TIME ); $dbh->disconnect; my $spammers_ref = $self->_find_spammers( $results, $SPAM_COUNT ); $results = undef; $self->_notify($spammers_ref) if @$spammers_ref; return 1; } sub _find_spammers { my ( $self, $results, $SPAM_COUNT ) = @_; my @output_lines = (); # The query itself doesn't take SPAM_COUNT into consideration; that # gets done right here.; exits foreach via 'last' bc results are # ORDER'd BY count(sender) in DESCending order foreach my $line (@$results) { last if $line->[1] < $SPAM_COUNT; push @output_lines, $line->[0]; } return \@output_lines; } sub _get_results { my ( $self, $dbh, $query, $ts, $SPAM_TIME ) = @_; my $results = $dbh->selectall_arrayref( $query, undef, $ts, $ts, $SPAM_TIME ); return $results; } sub _notify { my $self = shift; my $spammers_ref = shift; my $spammers = join( ', ', @$spammers_ref ); $logger->warn("The system has detected an unusually large amount of outbound email. The following sender(s) may be sending spam: $spammers") if (@$spammers_ref); require Cpanel::iContact::Class::Mail::SpammersDetected; require Cpanel::Notify; Cpanel::Notify::notification_class( constructor_args => [ origin => 'eximstats_spam_check', spammers => $spammers_ref ], map { $_ => q{Mail::SpammersDetected} } qw(class application), ); return; } 1;