3
0
mirror of https://github.com/pragma-/pbot.git synced 2024-10-04 18:38:47 +02:00

Track and handle nick-change events; add anti-nick-flood detection

This commit is contained in:
Pragmatic Software 2014-05-16 00:48:46 +00:00
parent 1f242aeec4
commit cd54dcb573
6 changed files with 85 additions and 9 deletions

View File

@ -49,7 +49,8 @@ sub initialize {
$self->{ENTER_ABUSE_MAX_OFFENSES} = 3;
$self->{ENTER_ABUSE_MAX_SECONDS} = 20;
$self->{channels} = {}; # per-channel statistics, e.g. for optimized tracking of last spoken nick for enter-abuse detection, etc
$self->{channels} = {}; # per-channel statistics, e.g. for optimized tracking of last spoken nick for enter-abuse detection, etc
$self->{nickflood} = {}; # statistics to track nickchange flooding
my $filename = delete $conf{banwhitelist_file} // $self->{pbot}->{data_dir} . '/ban_whitelist';
$self->{ban_whitelist} = PBot::DualIndexHashObject->new(name => 'BanWhitelist', filename => $filename);
@ -165,16 +166,22 @@ sub check_flood {
$channel = lc $channel;
my $mask = "$nick!$user\@$host";
$self->{pbot}->logger->log(sprintf("%-14s | %-65s | %s\n", $channel eq $mask ? "QUIT" : $channel, $mask, $text));
my $account = $self->{pbot}->{messagehistory}->get_message_account($nick, $user, $host);
if($mode == $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE}) {
$self->{pbot}->logger->log(sprintf("%-14s | %-65s | %s\n", "NICKCHANGE", $mask, $text));
$self->{nickflood}->{$account}->{changes}++;
} else {
$self->{pbot}->logger->log(sprintf("%-14s | %-65s | %s\n", $channel eq $mask ? "QUIT" : $channel, $mask, $text));
}
# handle QUIT events
# (these events come from $channel nick!user@host, not a specific channel or nick,
# so they need to be dispatched to all channels the nick has been seen on)
if($mode == $self->{pbot}->{messagehistory}->{MSG_DEPARTURE} and $text =~ /^QUIT/) {
my @channels = $self->{pbot}->{messagehistory}->{database}->get_channels($account);
foreach my $chan (@channels) {
next if $chan !~ m/^#/;
$self->check_join_watch($account, $chan, $text, $mode);
}
@ -183,7 +190,20 @@ sub check_flood {
return;
}
$self->check_join_watch($account, $channel, $text, $mode);
if($mode == $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE}) {
my @channels = $self->{pbot}->{messagehistory}->{database}->get_channels($account);
return if not @channels;
$channel = undef;
foreach my $chan (@channels) {
if($chan =~ m/^#/) {
$channel = $chan;
last;
}
}
return if not defined $channel;
} else {
$self->check_join_watch($account, $channel, $text, $mode);
}
# do not do flood processing for bot messages
if($nick eq $self->{pbot}->botnick) {
@ -275,6 +295,10 @@ sub check_flood {
my $joins = $self->{pbot}->{messagehistory}->{database}->get_recent_messages($account, $channel, $max_messages, $self->{pbot}->{messagehistory}->{MSG_JOIN});
$msg = $joins->[0];
}
elsif($mode == $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE}) {
my $nickchanges = $self->{pbot}->{messagehistory}->{database}->get_recent_messages($account, $channel, $max_messages, $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE});
$msg = $nickchanges->[0];
}
elsif($mode == $self->{pbot}->{messagehistory}->{MSG_DEPARTURE}) {
# no flood checks to be done for departure events
return;
@ -340,6 +364,22 @@ sub check_flood {
$self->{pbot}->logger->log("$nick msg flood offense " . $channel_data->{offenses} . " earned $length ignore\n");
$self->{pbot}->conn->privmsg($nick, "You have used too many commands in too short a time period, you have been ignored for $length.");
}
} elsif($mode == $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE} and $self->{nickflood}->{$account}->{changes} >= $max_messages) {
$self->{nickflood}->{$account}->{offenses}++;
$self->{nickflood}->{$account}->{changes} = $max_messages - 2; # allow 1 more change (to go back to original nick)
$self->{nickflood}->{$account}->{timestamp} = gettimeofday;
my $length = $self->{nickflood}->{$account}->{offenses} ** $self->{nickflood}->{$account}->{offenses} * $self->{nickflood}->{$account}->{offenses} * 60 * 4;
my @channels = $self->{pbot}->{messagehistory}->{database}->get_channels($account);
foreach my $chan (@channels) {
$self->{pbot}->chanops->ban_user_timed("*!$user\@$host", $chan, $length);
}
($nick) = $text =~ m/NICKCHANGE (.*)/;
$length = duration($length);
$self->{pbot}->logger->log("$nick nickchange flood offense " . $self->{nickflood}->{$account}->{offenses} . " earned $length ban\n");
$self->{pbot}->conn->privmsg($nick, "You have been temporarily banned due to nick-change flooding. You will be unbanned in $length.");
}
}
}
@ -672,6 +712,18 @@ sub adjust_offenses {
#$self->{pbot}->logger->log("[adjust-offenses] [$id][$channel] decreasing enter abuse offenses to $channel_data->{enter_abuses}\n");
$self->{pbot}->{messagehistory}->{database}->update_channel_data($id, $channel, $channel_data);
}
foreach my $account (keys %{ $self->{nickflood} }) {
if($self->{nickflood}->{$account}->{offenses} > 0 and gettimeofday - $self->{nickflood}->{$account}->{timestamp} >= 60 * 60 * 24) {
$self->{nickflood}->{$account}->{offenses}--;
if($self->{nickflood}->{$account}->{offenses} == 0) {
delete $self->{nickflood}->{$account};
} else {
$self->{nickflood}->{$account}->{timestamp} = gettimeofday;
}
}
}
}
1;

View File

@ -220,6 +220,7 @@ sub on_departure {
# QUIT messages must be dispatched to each channel the user is on
my @channels = $self->{pbot}->{messagehistory}->{database}->get_channels($message_account);
foreach my $chan (@channels) {
next if $chan !~ m/^#/;
$self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $chan, $text, $self->{pbot}->{messagehistory}->{MSG_DEPARTURE});
}
} else {
@ -236,6 +237,27 @@ sub on_departure {
}
}
sub on_nickchange {
my ($self, $conn, $event) = @_;
my ($nick, $user, $host, $newnick) = ($event->nick, $event->user, $event->host, $event->args);
$self->{pbot}->logger->log("$nick!$user\@$host changed nick to $newnick\n");
my $message_account = $self->{pbot}->{messagehistory}->{database}->get_message_account($nick, $user, $host);
$self->{pbot}->{messagehistory}->{database}->devalidate_all_channels($message_account);
my @channels = $self->{pbot}->{messagehistory}->{database}->get_channels($message_account);
foreach my $channel (@channels) {
next if $channel !~ m/^#/;
$self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $channel, "NICKCHANGE $newnick", $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE});
}
my $newnick_account = $self->{pbot}->{messagehistory}->{database}->get_message_account($newnick, $user, $host);
$self->{pbot}->{messagehistory}->{database}->devalidate_all_channels($newnick_account);
$self->{pbot}->{messagehistory}->{database}->update_hostmask_data($newnick_account, { last_seen => scalar gettimeofday });
$self->{pbot}->antiflood->check_flood("$nick!$user\@$host", $nick, $user, $host, "NICKCHANGE $newnick", 3, 60 * 60, $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE});
}
sub pbot {
my $self = shift;
return $self->{pbot};

View File

@ -39,9 +39,10 @@ sub initialize {
$self->{database}->begin();
$self->{database}->devalidate_all_channels();
$self->{MSG_CHAT} = 0; # PRIVMSG, ACTION
$self->{MSG_JOIN} = 1; # JOIN
$self->{MSG_DEPARTURE} = 2; # PART, QUIT, KICK
$self->{MSG_CHAT} = 0; # PRIVMSG, ACTION
$self->{MSG_JOIN} = 1; # JOIN
$self->{MSG_DEPARTURE} = 2; # PART, QUIT, KICK
$self->{MSG_NICKCHANGE} = 3; # CHANGED NICK
$self->{pbot}->commands->register(sub { $self->recall_message(@_) }, "recall", 0);
}

View File

@ -265,7 +265,7 @@ sub get_message_account {
return $id if defined $id;
my $rows = eval {
my $sth = $self->{dbh}->prepare('SELECT id,hostmask FROM Hostmasks WHERE hostmask LIKE ?');
my $sth = $self->{dbh}->prepare('SELECT id,hostmask FROM Hostmasks WHERE hostmask LIKE ? ORDER BY last_seen DESC');
$sth->bind_param(1, "$nick!%");
$sth->execute();
my $rows = $sth->fetchall_arrayref({});

View File

@ -203,6 +203,7 @@ sub connect {
$self->conn->add_handler('join' , sub { $self->irchandlers->on_join(@_) });
$self->conn->add_handler('kick' , sub { $self->irchandlers->on_kick(@_) });
$self->conn->add_handler('quit' , sub { $self->irchandlers->on_departure(@_) });
$self->conn->add_handler('nick' , sub { $self->irchandlers->on_nickchange(@_) });
$self->conn->add_handler('pong' , sub { $self->lagchecker->on_pong(@_) });
$self->conn->add_handler('whoisaccount' , sub { $self->antiflood->on_whoisaccount(@_) });
$self->conn->add_handler('banlist' , sub { $self->bantracker->on_banlist_entry(@_) });

View File

@ -13,7 +13,7 @@ use warnings;
# These are set automatically by the build/commit script
use constant {
BUILD_NAME => "PBot",
BUILD_REVISION => 575,
BUILD_REVISION => 576,
BUILD_DATE => "2014-05-15",
};