3
0
mirror of https://github.com/pragma-/pbot.git synced 2024-11-26 13:59:47 +01:00

Merge branch 'master' of github.com:pragma-/pbot

This commit is contained in:
Pragmatic Software 2021-07-13 14:58:28 -07:00
commit 355adcfced
2 changed files with 82 additions and 31 deletions

View File

@ -15,8 +15,8 @@ use parent 'PBot::Class';
use PBot::Imports; use PBot::Imports;
use DBI; use DBI;
use Carp qw(shortmess); use Carp qw/shortmess/;
use Time::HiRes qw(gettimeofday); use Time::HiRes qw/time/;
use Text::CSV; use Text::CSV;
use Text::Levenshtein qw/fastdistance/; use Text::Levenshtein qw/fastdistance/;
use Time::Duration; use Time::Duration;
@ -291,7 +291,7 @@ sub add_message_account {
eval { eval {
my $sth = $self->{dbh}->prepare('INSERT INTO Hostmasks VALUES (?, ?, ?, 0, ?, ?, ?)'); my $sth = $self->{dbh}->prepare('INSERT INTO Hostmasks VALUES (?, ?, ?, 0, ?, ?, ?)');
$sth->execute($mask, $id, scalar gettimeofday, $nick, $user, $host); $sth->execute($mask, $id, scalar time, $nick, $user, $host);
$self->{new_entries}++; $self->{new_entries}++;
if ((not defined $link_id) || ((defined $link_id) && ($link_type == $self->{alias_type}->{WEAK}))) { if ((not defined $link_id) || ((defined $link_id) && ($link_type == $self->{alias_type}->{WEAK}))) {
@ -393,77 +393,110 @@ sub get_message_account {
my $id = $self->get_message_account_id($mask); my $id = $self->get_message_account_id($mask);
return $id if defined $id; return $id if defined $id;
$self->{pbot}->{logger}->log("Getting new message account for $nick!$user\@$host...\n"); $self->{pbot}->{logger}->log("Getting new message account for $nick!$user\@$host\n");
$self->{pbot}->{logger}->log("Nick-changing from $orig_nick\n") if defined $orig_nick;
my $do_nothing = 0; my $do_nothing = 0;
my $sth; my $sth;
my ($rows, $link_type) = eval { my ($rows, $link_type) = eval {
my ($account1) = $host =~ m{/([^/]+)$}; my ($account1) = $host =~ m{/([^/]+)$};
$account1 = '' if not defined $account1; $account1 //= '';
# extract ips from hosts like 75-42-36-105.foobar.com as 75.42.36.105
my $hostip;
my $hostip = undef;
if ($host =~ m/(\d+[[:punct:]]\d+[[:punct:]]\d+[[:punct:]]\d+)\D/) { if ($host =~ m/(\d+[[:punct:]]\d+[[:punct:]]\d+[[:punct:]]\d+)\D/) {
$hostip = $1; $hostip = $1;
$hostip =~ s/[[:punct:]]/./g; $hostip =~ s/[[:punct:]]/./g;
} }
# nick-change from $orig_nick to $nick
if (defined $orig_nick) { if (defined $orig_nick) {
my $orig_id = $self->get_message_account_id("$orig_nick!$user\@$host"); # get original nick's account id
my @orig_nickserv_accounts = $self->get_nickserv_accounts($orig_id); my $orig_id = $self->get_message_account_id("$orig_nick!$user\@$host");
# changing nick to a Guest
if ($nick =~ m/^Guest\d+$/) { if ($nick =~ m/^Guest\d+$/) {
$sth = $self->{dbh}->prepare('SELECT id, hostmask, last_seen FROM Hostmasks WHERE user = ? and host = ? ORDER BY last_seen DESC'); # find most recent *!user@host, if any
$sth = $self->{dbh}->prepare('SELECT id, hostmask, last_seen FROM Hostmasks WHERE user = ? and host = ? ORDER BY last_seen DESC LIMIT 1');
$sth->execute($user, $host); $sth->execute($user, $host);
my $rows = $sth->fetchall_arrayref({}); my $rows = $sth->fetchall_arrayref({});
# found a probable match
if (defined $rows->[0]) { if (defined $rows->[0]) {
my $link_type = $self->{alias_type}->{STRONG}; my $link_type = $self->{alias_type}->{STRONG};
if (gettimeofday - $rows->[0]->{last_seen} > 60 * 60 * 48) {
# if 48 hours have elapsed since this *!user@host was seen
# then still link the Guest to this account, but weakly
if (time - $rows->[0]->{last_seen} > 60 * 60 * 48) {
$link_type = $self->{alias_type}->{WEAK}; $link_type = $self->{alias_type}->{WEAK};
$self->{pbot}->{logger}->log( $self->{pbot}->{logger}->log(
"Longer than 48 hours (" . concise duration(gettimeofday - $rows->[0]->{last_seen}) . ") for $rows->[0]->{hostmask} for $nick!$user\@$host, degrading to weak link\n"); "Longer than 48 hours (" . concise duration(time - $rows->[0]->{last_seen}) . ")"
. " for $rows->[0]->{hostmask} for $nick!$user\@$host, degrading to weak link\n"
);
} }
# log match and return link
$self->{pbot}->{logger}->log("6: nick-change guest match: $rows->[0]->{id}: $rows->[0]->{hostmask}\n"); $self->{pbot}->{logger}->log("6: nick-change guest match: $rows->[0]->{id}: $rows->[0]->{hostmask}\n");
$orig_nick = undef; $orig_nick = undef; # nick-change handled
return ($rows, $link_type); return ($rows, $link_type);
} }
} }
# find all accounts that match nick!*@*, sorted by last-seen
$sth = $self->{dbh}->prepare('SELECT id, hostmask, last_seen FROM Hostmasks WHERE nick = ? ORDER BY last_seen DESC'); $sth = $self->{dbh}->prepare('SELECT id, hostmask, last_seen FROM Hostmasks WHERE nick = ? ORDER BY last_seen DESC');
$sth->execute($nick); $sth->execute($nick);
my $rows = $sth->fetchall_arrayref({}); my $rows = $sth->fetchall_arrayref({});
# no nicks found, strongly link to original account
if (not defined $rows->[0]) { if (not defined $rows->[0]) {
$rows->[0] = {id => $orig_id, hostmask => "$orig_nick!$user\@$host"}; $rows->[0] = { id => $orig_id, hostmask => "$orig_nick!$user\@$host" };
$orig_nick = undef; # nick-change handled
return ($rows, $self->{alias_type}->{STRONG}); return ($rows, $self->{alias_type}->{STRONG});
} }
# look up original nick's NickServ accounts outside of upcoming loop
my @orig_nickserv_accounts = $self->get_nickserv_accounts($orig_id);
# go over the list of nicks and see if any identifying details match
my %processed_nicks; my %processed_nicks;
my %processed_akas; my %processed_akas;
foreach my $row (@$rows) { foreach my $row (@$rows) {
$self->{pbot}->{logger}->log("Found matching nickchange account: [$row->{id}] $row->{hostmask}\n"); $self->{pbot}->{logger}->log("Found matching nick-change account: [$row->{id}] $row->{hostmask}\n");
my ($tnick) = $row->{hostmask} =~ m/^([^!]+)!/; my ($tnick) = $row->{hostmask} =~ m/^([^!]+)!/;
# don't process duplicates
next if exists $processed_nicks{lc $tnick}; next if exists $processed_nicks{lc $tnick};
$processed_nicks{lc $tnick} = 1; $processed_nicks{lc $tnick} = 1;
# get all akas for this nick
my %akas = $self->get_also_known_as($tnick); my %akas = $self->get_also_known_as($tnick);
# check each aka for identifying details
foreach my $aka (keys %akas) { foreach my $aka (keys %akas) {
# skip dubious links
next if $akas{$aka}->{type} == $self->{alias_type}->{WEAK}; next if $akas{$aka}->{type} == $self->{alias_type}->{WEAK};
next if $akas{$aka}->{nickchange} == 1; next if $akas{$aka}->{nickchange} == 1;
# don't process duplicates
next if exists $processed_akas{$akas{$aka}->{id}}; next if exists $processed_akas{$akas{$aka}->{id}};
$processed_akas{$akas{$aka}->{id}} = 1; $processed_akas{$akas{$aka}->{id}} = 1;
$self->{pbot}->{logger}->log("Testing alias [$akas{$aka}->{id}] $aka\n"); $self->{pbot}->{logger}->log("Testing alias [$akas{$aka}->{id}] $aka\n");
my $match = 0; my $match = 0;
# account ids or *!user@host matches
if ($akas{$aka}->{id} == $orig_id || $aka =~ m/^.*!\Q$user\E\@\Q$host\E$/i) { if ($akas{$aka}->{id} == $orig_id || $aka =~ m/^.*!\Q$user\E\@\Q$host\E$/i) {
$self->{pbot}->{logger}->log("1: match: $akas{$aka}->{id} vs $orig_id // $aka vs *!$user\@$host\n"); $self->{pbot}->{logger}->log("1: match: $akas{$aka}->{id} vs $orig_id // $aka vs *!$user\@$host\n");
$match = 1; $match = 1;
goto MATCH; goto MATCH;
} }
# check if any nickserv accounts match
if (@orig_nickserv_accounts) { if (@orig_nickserv_accounts) {
my @nickserv_accounts = $self->get_nickserv_accounts($akas{$aka}->{id}); my @nickserv_accounts = $self->get_nickserv_accounts($akas{$aka}->{id});
foreach my $ns1 (@orig_nickserv_accounts) { foreach my $ns1 (@orig_nickserv_accounts) {
@ -477,6 +510,7 @@ sub get_message_account {
} }
} }
# check if hosts match
my ($thost) = $aka =~ m/@(.*)$/; my ($thost) = $aka =~ m/@(.*)$/;
if ($thost =~ m{/}) { if ($thost =~ m{/}) {
@ -487,11 +521,15 @@ sub get_message_account {
next; next;
} else { } else {
$self->{pbot}->{logger}->log("Cloaked hosts match: $host vs $thost\n"); $self->{pbot}->{logger}->log("Cloaked hosts match: $host vs $thost\n");
$rows->[0] = {id => $self->get_ancestor_id($akas{$aka}->{id}), hostmask => $aka}; $rows->[0] = {
id => $self->get_ancestor_id($akas{$aka}->{id}),
hostmask => $aka,
};
return ($rows, $self->{alias_type}->{STRONG}); return ($rows, $self->{alias_type}->{STRONG});
} }
} }
# fuzzy match hosts
my $distance = fastdistance($host, $thost); my $distance = fastdistance($host, $thost);
my $length = (length($host) > length($thost)) ? length $host : length $thost; my $length = (length($host) > length($thost)) ? length $host : length $thost;
@ -530,12 +568,12 @@ sub get_message_account {
my $new_id = $self->add_message_account($mask); my $new_id = $self->add_message_account($mask);
$self->link_alias($orig_id, $new_id, $self->{alias_type}->{WEAK}); $self->link_alias($orig_id, $new_id, $self->{alias_type}->{WEAK});
$self->update_hostmask_data($mask, {nickchange => 1, last_seen => scalar gettimeofday}); $self->update_hostmask_data($mask, {nickchange => 1, last_seen => scalar time});
$do_nothing = 1; $do_nothing = 1;
$rows->[0] = {id => $new_id}; $rows->[0] = {id => $new_id};
return ($rows, 0); return ($rows, 0);
} } # end nick-change
if ($host =~ m{^gateway/web/irccloud.com}) { if ($host =~ m{^gateway/web/irccloud.com}) {
$sth = $self->{dbh}->prepare('SELECT id, hostmask, last_seen FROM Hostmasks WHERE host = ? ORDER BY last_seen DESC'); $sth = $self->{dbh}->prepare('SELECT id, hostmask, last_seen FROM Hostmasks WHERE host = ? ORDER BY last_seen DESC');
@ -576,10 +614,10 @@ sub get_message_account {
my $rows = $sth->fetchall_arrayref({}); my $rows = $sth->fetchall_arrayref({});
if (defined $rows->[0]) { if (defined $rows->[0]) {
my $link_type = $self->{alias_type}->{STRONG}; my $link_type = $self->{alias_type}->{STRONG};
if (gettimeofday - $rows->[0]->{last_seen} > 60 * 60 * 48) { if (time - $rows->[0]->{last_seen} > 60 * 60 * 48) {
$link_type = $self->{alias_type}->{WEAK}; $link_type = $self->{alias_type}->{WEAK};
$self->{pbot}->{logger}->log( $self->{pbot}->{logger}->log(
"Longer than 48 hours (" . concise duration(gettimeofday - $rows->[0]->{last_seen}) . ") for $rows->[0]->{hostmask} for $nick!$user\@$host, degrading to weak link\n"); "Longer than 48 hours (" . concise duration(time - $rows->[0]->{last_seen}) . ") for $rows->[0]->{hostmask} for $nick!$user\@$host, degrading to weak link\n");
} }
$self->{pbot}->{logger}->log("6: guest match: $rows->[0]->{id}: $rows->[0]->{hostmask}\n"); $self->{pbot}->{logger}->log("6: guest match: $rows->[0]->{id}: $rows->[0]->{hostmask}\n");
return ($rows, $link_type); return ($rows, $link_type);
@ -667,10 +705,10 @@ sub get_message_account {
$sth->execute($user, $host); $sth->execute($user, $host);
$rows = $sth->fetchall_arrayref({}); $rows = $sth->fetchall_arrayref({});
if (defined $rows->[0] and gettimeofday - $rows->[0]->{last_seen} > 60 * 60 * 48) { if (defined $rows->[0] and time - $rows->[0]->{last_seen} > 60 * 60 * 48) {
$link_type = $self->{alias_type}->{WEAK}; $link_type = $self->{alias_type}->{WEAK};
$self->{pbot}->{logger}->log( $self->{pbot}->{logger}->log(
"Longer than 48 hours (" . concise duration(gettimeofday - $rows->[0]->{last_seen}) . ") for $rows->[0]->{hostmask} for $nick!$user\@$host, degrading to weak link\n"); "Longer than 48 hours (" . concise duration(time - $rows->[0]->{last_seen}) . ") for $rows->[0]->{hostmask} for $nick!$user\@$host, degrading to weak link\n");
} }
=cut =cut
@ -684,20 +722,28 @@ sub get_message_account {
return ($rows, $link_type); return ($rows, $link_type);
}; };
$self->{pbot}->{logger}->log($@) if $@;
if (my $exception = $@) {
$self->{pbot}->{logger}->log("Exception getting account: $exception");
}
# nothing else to do here for nick-change, return id
return $rows->[0]->{id} if $do_nothing; return $rows->[0]->{id} if $do_nothing;
if (defined $rows->[0] and not defined $orig_nick) { if (defined $rows->[0] and not defined $orig_nick) {
if ($link_type == $self->{alias_type}->{STRONG}) { if ($link_type == $self->{alias_type}->{STRONG}) {
my $host1 = lc "$nick!$user\@$host"; my $host1 = lc "$nick!$user\@$host";
my $host2 = lc $rows->[0]->{hostmask}; my $host2 = lc $rows->[0]->{hostmask};
my ($nick1) = $host1 =~ m/^([^!]+)!/; my ($nick1) = $host1 =~ m/^([^!]+)!/;
my ($nick2) = $host2 =~ m/^([^!]+)!/; my ($nick2) = $host2 =~ m/^([^!]+)!/;
my $distance = fastdistance($nick1, $nick2); my $distance = fastdistance($nick1, $nick2);
my $length = (length $nick1 > length $nick2) ? length $nick1 : length $nick2; my $length = (length $nick1 > length $nick2) ? length $nick1 : length $nick2;
if ($distance > 1 && ($nick1 !~ /^guest/ && $nick2 !~ /^guest/) && ($host1 !~ /unaffiliated/ || $host2 !~ /unaffiliated/)) { my $irc_cloak = $self->{pbot}->{registry}->get_value('irc', 'cloak') // 'user';
if ($distance > 1 && ($nick1 !~ /^guest/ && $nick2 !~ /^guest/) && ($host1 !~ /$irc_cloak/ || $host2 !~ /$irc_cloak/)) {
my $id = $rows->[0]->{id}; my $id = $rows->[0]->{id};
$self->{pbot}->{logger}->log("[$nick1][$nick2] $distance / $length\n"); $self->{pbot}->{logger}->log("[$nick1][$nick2] $distance / $length\n");
$self->{pbot}->{logger}->log("Possible bogus account: ($id) $host1 vs ($id) $host2\n"); $self->{pbot}->{logger}->log("Possible bogus account: ($id) $host1 vs ($id) $host2\n");
@ -705,20 +751,24 @@ sub get_message_account {
} }
$self->{pbot}->{logger}->log("message-history: [get-account] $nick!$user\@$host " $self->{pbot}->{logger}->log("message-history: [get-account] $nick!$user\@$host "
. ($link_type == $self->{alias_type}->{WEAK} ? "weakly linked to" : "added to account") . ($link_type == $self->{alias_type}->{WEAK} ? "weakly linked to" : "added to account")
. " $rows->[0]->{hostmask} with id $rows->[0]->{id}\n"); . " $rows->[0]->{hostmask} with id $rows->[0]->{id}\n"
);
$self->add_message_account("$nick!$user\@$host", $rows->[0]->{id}, $link_type); $self->add_message_account("$nick!$user\@$host", $rows->[0]->{id}, $link_type);
$self->devalidate_all_channels($rows->[0]->{id}); $self->devalidate_all_channels($rows->[0]->{id});
$self->update_hostmask_data("$nick!$user\@$host", {last_seen => scalar gettimeofday}); $self->update_hostmask_data("$nick!$user\@$host", { last_seen => scalar time });
my @nickserv_accounts = $self->get_nickserv_accounts($rows->[0]->{id}); my @nickserv_accounts = $self->get_nickserv_accounts($rows->[0]->{id});
foreach my $nickserv_account (@nickserv_accounts) { foreach my $nickserv_account (@nickserv_accounts) {
$self->{pbot}->{logger}->log("$nick!$user\@$host [$rows->[0]->{id}] seen with nickserv account [$nickserv_account]\n"); $self->{pbot}->{logger}->log("$nick!$user\@$host [$rows->[0]->{id}] seen with nickserv account [$nickserv_account]\n");
$self->{pbot}->{antiflood}->check_nickserv_accounts($nick, $nickserv_account, "$nick!$user\@$host"); $self->{pbot}->{antiflood}->check_nickserv_accounts($nick, $nickserv_account, "$nick!$user\@$host");
} }
return $rows->[0]->{id}; return $rows->[0]->{id};
} }
$self->{pbot}->{logger}->log("No account found for mask [$mask], adding new account\n"); $self->{pbot}->{logger}->log("No account found for $mask, adding new account\n");
return $self->add_message_account($mask); return $self->add_message_account($mask);
} }
@ -1342,7 +1392,7 @@ sub link_aliases {
$sth->execute($host); $sth->execute($host);
my $rows = $sth->fetchall_arrayref({}); my $rows = $sth->fetchall_arrayref({});
my $now = gettimeofday; my $now = time;
foreach my $row (@$rows) { foreach my $row (@$rows) {
my $idhost = $self->find_most_recent_hostmask($row->{id}) if $debug_link >= 2 && $row->{id} != $account; my $idhost = $self->find_most_recent_hostmask($row->{id}) if $debug_link >= 2 && $row->{id} != $account;
@ -1491,7 +1541,8 @@ sub link_alias {
my ($nick2) = $host2 =~ m/^([^!]+)!/; my ($nick2) = $host2 =~ m/^([^!]+)!/;
my $distance = fastdistance($nick1, $nick2); my $distance = fastdistance($nick1, $nick2);
my $length = (length $nick1 > length $nick2) ? length $nick1 : length $nick2; my $length = (length $nick1 > length $nick2) ? length $nick1 : length $nick2;
if ($distance > 1 && ($nick1 !~ /^guest/ && $nick2 !~ /^guest/) && ($host1 !~ /unaffiliated/ || $host2 !~ /unaffiliated/)) { my $irc_cloak = $self->{pbot}->{registry}->get_value('irc', 'cloak') // 'user';
if ($distance > 1 && ($nick1 !~ /^guest/ && $nick2 !~ /^guest/) && ($host1 !~ /$irc_cloak/ || $host2 !~ /$irc_cloak/)) {
$self->{pbot}->{logger}->log("[$nick1][$nick2] $distance / $length\n"); $self->{pbot}->{logger}->log("[$nick1][$nick2] $distance / $length\n");
$self->{pbot}->{logger}->log("Possible bogus link: ($id) $host1 vs ($alias) $host2\n"); $self->{pbot}->{logger}->log("Possible bogus link: ($id) $host1 vs ($alias) $host2\n");
} }

View File

@ -16,8 +16,8 @@ use LWP::UserAgent;
# These are set automatically by the misc/update_version script # These are set automatically by the misc/update_version script
use constant { use constant {
BUILD_NAME => "PBot", BUILD_NAME => "PBot",
BUILD_REVISION => 4173, BUILD_REVISION => 4176,
BUILD_DATE => "2021-07-10", BUILD_DATE => "2021-07-11",
}; };
sub initialize { sub initialize {