App-BlockWebFlooders

 view release on metacpan or  search on metacpan

script/block-web-flooders  view on Meta::CPAN


sub action_unblock_all {
    _init();

    local @ARGV = keys %Blocked;
    _block_or_unblock_ips("unblock");
    _set_need_reload(1);
}

sub action_run {
    #require Term::Size;
    require Time::Duration;

    #my ($columns, $rows) = Term::Size::chars *STDOUT{IO};

    my $last_check_spanel_log_time;
    my ($spanel_http_log_name, $spanel_https_log_name);

    my $last_update_output_time;
    my $last_unblock_time;
    my $last_reload_data_time;
    my $num_lines = 0;

    _init();

    local *INPUT;
    if (defined $Opts{spanel_site}) {
        require Tie::Handle::TailSwitch;
        my $dir = "/s/$Opts{spanel_site}/syslog";
        tie *INPUT, 'Tie::Handle::TailSwitch', (
            globs => ["$dir/https_access.*.log", "$dir/http_access.*.log"],
        );
    } else {
        *INPUT = \*STDIN;
    }

  LINE:
    while (1) {
        my $line = <INPUT>;
        if (!defined($line) || !length($line)) {
            sleep 0.5;
            next;
        }

        my $now = time();
        $num_lines++;
        chomp $line;
        $line =~ /\A($RE{ipv4})\s/ or do {
            warn "$PROG: Line '$line': Can't parse IP address, skipped\n";
            next;
        };
        my $ip = $1;
        next if $Blocked{$ip};

      OUTPUT:
        {
            last unless !$last_update_output_time ||
                $last_update_output_time <= $now-2;
            print "\e[2J\e[;H"; # clear screen + put cursor at top (0,0)
            printf "Blocked IPs: %s%4d%s | Log lines: %s%6d%s | Running for: %s%s%s\n",
                color('bold'), (scalar keys %Blocked), color('reset'),
                color('bold'), $num_lines, color('reset'),
                color('bold'), Time::Duration::concise(Time::Duration::duration($now-$^T, 2)), color('reset');
            $last_update_output_time = $now;
            printf "Top IPs:\n";
            my $i = 0;
            for my $ip (sort { scalar(@{ $Ips{$b} }) <=> scalar(@{ $Ips{$a} }) } keys %Ips) {
                last if $i++ >= 10;
                printf "  %15s (%4d)\n", $ip, scalar(@{ $Ips{$ip} });
            }
            printf "Last messages:\n";
            $i = 0;
            for my $msg (@Messages) {
                last if $i++ >= 5;
                print "  $msg\n";
            }
        } # OUTPUT

      UNBLOCK:
        {
            last unless !$last_unblock_time ||
                $last_unblock_time <= $now-60;
            for (keys %Blocked) {
                next unless $Blocked{$_} < $now - $Opts{block_period};
                unblock_ip($_);
            }
            $last_unblock_time = $now;
        } # UNBLOCK

      RELOAD_DATA:
        {
            last unless !$last_reload_data_time ||
                $last_reload_data_time <= $now-5;
            _reload_data_from_db();
            $last_reload_data_time = $now;
        }

        for my $has (@{ $Opts{has} }) {
            next LINE unless index($line, $has) >= 0;
        }
        for my $lacks (@{ $Opts{lacks} }) {
            next LINE if index($line, $lacks) >= 0;
        }
        for my $pat (@{ $Opts{has_pattern} }) {
            next LINE unless $line =~ $pat;
        }
        for my $pat (@{ $Opts{lacks_pattern} }) {
            next LINE if $line =~ $pat;
        }

        $Ips{$ip} //= do {
            tie my @ary, "Tie::Array::Expire", $Opts{period};
            \@ary;
        };
        push @{ $Ips{$ip} }, 1;
        if (@{ $Ips{$ip} } > $Opts{limit} && !$Whitelisted{$ip}) {
            block_ip($ip);
            delete $Ips{$ip};
        }
    } # loop
}

# MAIN



( run in 0.530 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )