diff options
| author | 2013-03-07 10:35:43 +0000 | |
|---|---|---|
| committer | 2013-03-07 10:35:43 +0000 | |
| commit | fdb1d6257cb9871c687e13b1ac1ec038ed2529e4 (patch) | |
| tree | 94b21da667654e56d20b3ba0e194a6a846226d61 | |
| parent | 28e8eda8a99d2ea148741b2783b4f6110a8927d8 (diff) | |
Added logging of kicks/bans/quiets/removes/klines/kills to a special SQL table and corresponding text files
Enabled SQL debugging
Bugfix: Only attempt to determine a host's IP if it doesn't contain a '/'
Updates to channels.xml and users.xml
Adjusted ;userx add and ;userx flags such that A cannot give B a flag that A doesn't already have
Tweaked the ;help command
Fixed ;mship such that it will respond even if it can't see the nick provided.
Tweaked ;status to give output in format like 7d22h18m3s instead of 9814798712 seconds
Added a ;teredo helper command to give info on IPv6 teredo-tunneled connections
Added a nick blacklist file (to counter bot nicklists).
Added a english wordlist file, for "garbage" detection.
Added ;investigate and ;investigate2 commands
Added a way to not throttle info-risk threats
Added special detection for a cycling botnet
Added special detection for bots that join, say something, and immediately quit
Added detection for ascii art
Added detection for "garbage" text
Added fuzzy-matching against a set of nicks
Added "real IP" to state tracking and logging, which "decrypts" gateway/web and teredo IPs
Moved sigalarm code into meta.pl
Improved statsp tracking, and logs it to a file
Ping-pong every 30 seconds, auto-reconnect on persistent lag.
Ensure inspector routine is always called AFTER log-handling routines
Fixed a state-tracking bug in topic change handling
Fixed a state-tracking bug with nick changes
Fixed some state-tracking bugs with mode changes
Determine who is impacted when a quiet/ban mask is placed
Fixed handling of CTCP SOURCE requests
Added feature where it keeps a 30 line "backlog" of each channel in memory.
Added the reason for parts and quits to text logging
| -rw-r--r-- | config-default/channels.xml | 187 | ||||
| -rw-r--r-- | config-default/commands.xml | 157 | ||||
| -rw-r--r-- | config-default/mysql.xml | 1 | ||||
| -rw-r--r-- | config-default/settings.xml | 1 | ||||
| -rw-r--r-- | config-default/users.xml | 26 | ||||
| -rwxr-xr-x | meta.pl | 31 | ||||
| -rw-r--r-- | modules/classes.pl | 75 | ||||
| -rw-r--r-- | modules/command.pl | 1 | ||||
| -rw-r--r-- | modules/event.pl | 244 | ||||
| -rw-r--r-- | modules/inspect.pl | 8 | ||||
| -rw-r--r-- | modules/log.pl | 47 | ||||
| -rw-r--r-- | modules/mysql.pl | 121 | ||||
| -rw-r--r-- | modules/util.pl | 41 |
13 files changed, 762 insertions, 178 deletions
diff --git a/config-default/channels.xml b/config-default/channels.xml index 178a0bb..830a0eb 100644 --- a/config-default/channels.xml +++ b/config-default/channels.xml @@ -41,10 +41,17 @@ <debug>##hamradio-ops</debug> </msgs> </channel> + <channel id="##hamradio-banappeal"> + <hilights></hilights> + </channel> <channel id="##hamradio-ops"> <hilights></hilights> <msgs></msgs> </channel> + <channel id="##hardware"> + <hilights></hilights> + <msgs></msgs> + </channel> <channel id="##linux" silence="yes"> <hilights> <info>Dominian</info> @@ -106,6 +113,7 @@ <channel id="##wikia"> <hilights> <info>vegadark</info> + <info>Furry</info> </hilights> <msgs></msgs> </channel> @@ -117,6 +125,7 @@ <disable>Thehelpfulone</disable> <high>werdan7</high> <info>njan</info> + <info>PZt</info> <low>numist</low> <low>HentaiXP</low> <low>TechSalvager</low> @@ -138,8 +147,15 @@ <hilights></hilights> <msgs></msgs> </channel> - <channel id="#antispammeta-debug" /> - <channel id="#baadf00d" /> + <channel id="#antispammeta-debug"> + <hilights></hilights> + </channel> + <channel id="#baadf00d"> + <hilights></hilights> + </channel> + <channel id="#chromium-support"> + <hilights></hilights> + </channel> <channel id="#cisco"> <hilights></hilights> <msgs></msgs> @@ -196,7 +212,9 @@ <hilights></hilights> </channel> <channel id="#frikipedia"> - <hilights></hilights> + <hilights> + <info>Nietzsche</info> + </hilights> <msgs></msgs> </channel> <channel id="#gentoo" silence="yes"> @@ -233,12 +251,10 @@ </channel> <channel id="#mediawiki" silence="yes"> <hilights> - <debug>flyingparchment</debug> - <debug>roberthl</debug> <debug>Snowolf</debug> - <disable>seanw</disable> + <debug>techman224</debug> <info>charitwo</info> - <low>vvv</low> + <medium>Jasper_Deng</medium> </hilights> <msgs> <debug>#wikimedia-ops</debug> @@ -255,14 +271,13 @@ </channel> <channel id="#persian"> <hilights></hilights> + <msgs></msgs> </channel> <channel id="#persians"> <hilights></hilights> </channel> <channel id="#reddit"> <hilights> - <debug>KyleXY</debug> - <debug>kylexy</debug> <debug>TheMoonMaster</debug> <debug>Paradox</debug> <debug>Mortvert</debug> @@ -333,20 +348,20 @@ </channel> <channel id="#wikimedia" silence="yes"> <hilights> - <debug>Martinp23</debug> <debug>PeterSymonds</debug> - <debug>vvv</debug> <debug>AfterDeath</debug> - <debug>DeltaQuad</debug> <debug>Snowolf</debug> <debug>Thehelpfulone</debug> <debug>Tanvir</debug> <debug>jeremyb</debug> <debug>Logan_</debug> + <debug>Rjd0060</debug> <info>charitwo</info> <info>Rjd0060</info> <info>Fluffernutter</info> <info>TBloemink</info> + <info>Steven_Zhang</info> + <low>DeltaQuad</low> </hilights> <msgs> <debug>#wikimedia-ops</debug> @@ -360,12 +375,29 @@ <debug>Thehelpfulone</debug> <debug>Snowolf</debug> <debug>Logan_</debug> - <low>Kanonkas</low> + <debug>Rjd0060</debug> + <info>James_F</info> </hilights> <msgs> <debug>#wikimedia-ops</debug> </msgs> </channel> + <channel id="#wikimedia-irc"> + <hilights> + <info>seanw</info> + <info>martinp23</info> + <info>Rjd0060</info> + <info>Cbrown1023</info> + <info>dungodung</info> + <info>PeterSymonds</info> + <info>Barras</info> + <info>Thehelpfulone</info> + </hilights> + <msgs> + <debug>#wikimedia-ops</debug> + <low>#wikimedia-ops</low> + </msgs> + </channel> <channel id="#wikimedia-office"> <hilights> <info>mbimmler</info> @@ -378,21 +410,17 @@ <info>Theo10011</info> <info>Ironholds</info> </hilights> - <msgs> - <debug>#wikimedia-ops</debug> - </msgs> + <msgs></msgs> </channel> <channel id="#wikimedia-ops"> <hilights> <debug>Cbrown1023</debug> <debug>Thehelpfulone</debug> - <debug>Not_the_NSA</debug> - <debug>Kanonkas</debug> <debug>PeterSymonds</debug> - <debug>AfterDeath</debug> <debug>Logan_</debug> <debug>jeremyb</debug> <debug>AfterDeath</debug> + <debug>Snowolf</debug> <info>charitwo</info> <info>TBloemink</info> <info>Mh7kJ</info> @@ -427,9 +455,7 @@ <debug>fschulenburg</debug> <debug>Bastique</debug> </hilights> - <msgs> - <debug>#wikimedia-ops</debug> - </msgs> + <msgs></msgs> </channel> <channel id="#wikimedia-overflow"> <hilights> @@ -445,6 +471,7 @@ <debug>PeterSymonds</debug> <debug>Snowolf</debug> <debug>Thehelpfulone</debug> + <debug>Rjd0060</debug> <info>Snowolf</info> <info>Rjd0060</info> <info>TBloemink</info> @@ -464,11 +491,19 @@ <debug>Thehelpfulone</debug> <debug>Snowolf</debug> <debug>Rjd0060</debug> + <debug>techman224</debug> + <medium>Jasper_Deng</medium> </hilights> <msgs> <low>#wikimedia-ops</low> </msgs> </channel> + <channel id="#wikimedia-techJasper_Deng"> + <hilights></hilights> + </channel> + <channel id="#wikimedia-techjasper_deng"> + <hilights></hilights> + </channel> <channel id="#wikimedia-toolserver"> <hilights> <info>Austin</info> @@ -481,53 +516,36 @@ <info>Simetrical</info> <info>Werdna</info> </hilights> - <msgs> - <debug>#wikimedia-ops</debug> - </msgs> + <msgs></msgs> </channel> <channel id="#wikipedia" silence="yes"> <hilights> - <debug>Golbez</debug> <debug>Prodego</debug> <debug>Snowolf</debug> <debug>AfterDeath</debug> <debug>Thehelpfulone</debug> <debug>werdan7</debug> - <debug>wimt</debug> - <debug>Jake_Wartenberg</debug> <debug>shimgray</debug> - <debug>kibble</debug> <debug>PeterSymonds</debug> <debug>Jamesofur</debug> <debug>killiondude</debug> <debug>SpitfireWP</debug> <debug>jeremyb</debug> - <debug>Maximillion</debug> <debug>stwalkerster</debug> - <debug>DeltaQuad</debug> <debug>Gfoley4</debug> <debug>Logan_</debug> - <debug>Theo10011</debug> <debug>Snowolf</debug> <debug>Tanvir</debug> <debug>TBloemink</debug> <debug>Rjd0060</debug> <debug>Shirik</debug> - <low>bumm13_</low> - <low>Cyrius</low> + <info>Steven_Zhang</info> <low>DanielB</low> <low>FastLizard4</low> <low>James_F</low> <low>JohnReaves</low> - <low>Lucifer_Cat</low> - <low>Luna-San</low> - <low>Mike42</low> - <low>Mike_H</low> - <low>skenmy</low> - <low>ST47</low> - <low>tawker</low> <low>slakr</low> - <low>Courcelles</low> + <low>DeltaQuad</low> <medium>closedmouth</medium> <medium>Fluffernutter</medium> </hilights> @@ -555,8 +573,6 @@ <debug>Jamesofur</debug> <debug>SpitfireWP</debug> <debug>jeremyb</debug> - <debug>DeltaQuad</debug> - <debug>Theo10011</debug> <debug>Gfoley4</debug> <debug>Logan_</debug> <debug>Snowolf</debug> @@ -564,12 +580,10 @@ <debug>Shirik</debug> <debug>TBloemink</debug> <debug>Rjd0060</debug> - <low>Cobi</low> - <low>Golbez</low> + <info>DeltaQuad</info> <low>agkwiki</low> <low>KFP</low> <low>slakr</low> - <low>Courcelles</low> <medium>closedmouth</medium> <medium>Fluffernutter</medium> </hilights> @@ -581,41 +595,21 @@ <hilights> <info>Thehelpfulone</info> <info>jamesofur</info> - <info>Nixeagle</info> <info>DeltaQuad</info> - <info>Netalarm</info> - <info>JoeGazz84</info> - <info>MacMed</info> <info>Snowolf</info> </hilights> - <msgs> - <debug>#wikimedia-ops</debug> - <low>#wikimedia-ops</low> - </msgs> + <msgs></msgs> </channel> <channel id="#wikipedia-en-ambassadors"> <hilights> - <info>anowlin</info> - <info>ragesoss</info> <info>stwalkerster</info> - <info>chzz</info> <info>Prodego</info> - <info>Deskana</info> - <info>Pathoschild</info> - <info>fetchcomms</info> - <info>BarkingFish</info> - <info>Cbrown1023</info> - <info>Earwig</info> - <info>ldavis</info> - <info>annielin</info> <info>PeterSymonds</info> <info>Shirik</info> <info>Fluffernutter</info> <info>Thehelpfulone</info> </hilights> - <msgs> - <debug>#wikimedia-ops</debug> - </msgs> + <msgs></msgs> </channel> <channel id="#wikipedia-en-classroom"> <hilights></hilights> @@ -623,25 +617,13 @@ <channel id="#wikipedia-en-help" silence="yes"> <hilights> <debug>werdan7</debug> - <debug>GDonato</debug> <debug>Thehelpfulone</debug> - <debug>Mike42</debug> - <debug>bjelleklang</debug> - <debug>JohnReaves</debug> - <debug>After-Midnight</debug> - <debug>Srikeit</debug> - <debug>Deon555</debug> - <debug>Luna-San</debug> - <debug>Golbez</debug> <debug>stwalkerster</debug> <debug>PeterSymonds</debug> - <debug>Hersfold</debug> <debug>killiondude</debug> - <debug>DeltaQuad</debug> <debug>Logan_</debug> <debug>Shirik</debug> <debug>slakr</debug> - <debug>JoeGazz84</debug> <debug>Steven_Zhang</debug> <debug>SteveMobile</debug> <debug>TBloemink</debug> @@ -649,11 +631,13 @@ <debug>Tanvir</debug> <debug>TBloemink</debug> <debug>Ocaasi</debug> + <debug>Rjd0060</debug> <info>mabdul</info> + <info>gwickwire</info> <low>KFP</low> <low>Gfoley4</low> - <low>sonia</low> <low>Pine</low> + <low>DeltaQuad</low> </hilights> <msgs> <debug>#wikimedia-ops</debug> @@ -672,12 +656,35 @@ <info>PeterSymonds</info> <info>Shirik</info> </hilights> + <msgs></msgs> + </channel> + <channel id="#wikipedia-media"> + <hilights></hilights> + </channel> + <channel id="#wikipedia-otrs"> + <hilights></hilights> + </channel> + <channel id="#wikipedia-social" silence="yes"> + <hilights></hilights> + <msgs> + <low>#wikimedia-ops</low> + </msgs> + </channel> + <channel id="#wikitravel"> + <hilights></hilights> + </channel> + <channel id="#wikivoyage"> + <hilights> + <debug>Snowolf</debug> + <debug>Rschen7754</debug> + <info>Thehelpfulone</info> + <info>Logan_</info> + </hilights> <msgs> <debug>#wikimedia-ops</debug> - <low>#wikimedia-ops</low> </msgs> </channel> - <channel id="#wikipedia-social" silence="yes"> + <channel id="#wikivoyage-es"> <hilights></hilights> <msgs> <low>#wikimedia-ops</low> @@ -698,11 +705,14 @@ <info>Wytukaze</info> <info>Tawker</info> </hilights> - <msgs> - <debug>#wikimedia-ops</debug> - </msgs> + <msgs></msgs> + </channel> + <channel id="antispammeta"> + <hilights></hilights> + </channel> + <channel id="antispammetabeta"> + <hilights></hilights> </channel> - <channel id="antispammetabeta" /> <channel id="default"> <hilights></hilights> </channel> @@ -714,7 +724,6 @@ <medium>dave2</medium> </hilights> <msgs> - <debug>##asb-nexus</debug> <debug>#antispammeta</debug> </msgs> </channel> diff --git a/config-default/commands.xml b/config-default/commands.xml index a3a1695..8154271 100644 --- a/config-default/commands.xml +++ b/config-default/commands.xml @@ -1,19 +1,69 @@ <commands> - <command cmd="^;status$" flag="o"> + <command cmd="^;makemelunch"> + <![CDATA[ + $conn->me($event->replyto, "makes " . $event->{nick} . " a sandwich"); + ]]> + </command> + <command cmd="^;teredo (\S+)"> + <![CDATA[ + my $arg1 = $1; + my @splitip = split(/:/, $arg1); + if ( (int($splitip[0]) != 2001) || (int($splitip[1]) != 0) ) { + $conn->privmsg($event->replyto, "This is not a teredo-tunnelled IP."); + return; + } + print Dumper(\@splitip); + my $server = join('.', unpack('C4', pack('N', hex($splitip[2] . $splitip[3])))); + my $host = join('.', unpack('C4', pack('N', (hex($splitip[6] . $splitip[7])^hex('ffffffff'))))); + my $port = hex($splitip[5]) ^ hex('ffff'); + $conn->privmsg($event->replyto, "Source is $host:$port; teredo server in use is $server."); +#hex('41379e76') ^ hex('ffffffff'); print join ('.', unpack('C4', pack('N', $ip))) . "\n" +#join '.', unpack "C*", pack "H*", $ip; + #2001:0:4137:9e76:3094:127d:51a2:6952 + #2001:0 - teredo marker + #4137:9e76 - teredo server + #3094 - teredo flags + #127d - xor 0xff - UDP port in use + #51a2:6952 - xor 0xff - source IP + ]]> + </command> + <command cmd="^;status$"> <![CDATA[ my $size = `ps -p $$ h -o size`; my $cputime = `ps -p $$ h -o time`; chomp $size; chomp $cputime; - $conn->privmsg($event->replyto, "This bot has been running for " . (time - $::starttime) . " seconds" . + my $upstr = ''; + my $up = (time - $::starttime); + if (int($up/86400) != 0) { #days + $upstr = $upstr . int($up/86400) . 'd'; + $up = $up % 86400; + } + if (int($up/3600) != 0) { #hours + $upstr = $upstr . int($up/3600) . 'h'; + $up = $up % 3600; + } + if (int($up/60) != 0) { #minutes + $upstr = $upstr . int($up/60) . 'm'; + $up = $up % 60; + } + if (int($up/1) != 0) { #seconds + $upstr = $upstr . int($up/1) . 's'; + $up = $up % 1; + } + $conn->privmsg($event->replyto, "This bot has been running for " . $upstr . ", is tracking " . (scalar (keys %::sn)) . " nicks" . " across " . (scalar (keys %::sc)) . " tracked channels." . " It is using " . $size . "KB of RAM" . " and has used " . $cputime . " of CPU time."); ]]> </command> - <command cmd="^;mship (\S+)$" flag="c"> + <command cmd="^;mship (\S+)$" flag="s"> <![CDATA[ - $conn->privmsg($event->replyto, $1 . " is on: " . ASM::Util->commaAndify(sort @{$::sn{lc $1}->{mship}})); + if (defined($::sn{lc $1}->{mship})) { + $conn->privmsg($event->replyto, $1 . " is on: " . ASM::Util->commaAndify(sort @{$::sn{lc $1}->{mship}})); + } else { + $conn->privmsg($event->replyto, "I don't see $1."); + } ]]> </command> <command cmd="^;source$"> @@ -46,8 +96,8 @@ </command> <command cmd="^;help$"> <![CDATA[ - $conn->privmsg($event->replyto, "help is at http://meta.wikimedia.org/wiki/User:WHeimbigner/AntiSpamMeta"); - $conn->privmsg($event->replyto, "You can also get faster help by emailing william dot heimbigner at ttu dot edu - or bug ErrantEgo or tomaw"); + $conn->privmsg($event->replyto, "command list is at http://antispammeta.net/syntax.txt ; see also http://meta.wikimedia.org/wiki/User:WHeimbigner/AntiSpamMeta (not as up to date but contains some additonal info)"); + $conn->privmsg($event->replyto, "You can also get faster help by bugging ow, DLa\x02\x02nge, tom\x02\x02aw, or mari\x02\x02enz"); ]]> </command> <command cmd="^;db$"> @@ -63,10 +113,95 @@ $conn->privmsg($event->replyto, "$result results found."); ]]> </command> + <command cmd="^;investigate (\S+) *$"> + <![CDATA[ + my $nick = lc $1; + unless (defined($::sn{$nick})) { + $conn->privmsg($event->replyto, "I don't see $nick in my state tracking database, so I can't run any queries on their info, sorry :(" . + " You can try https://antispammeta.net/cgi-bin/secret/investigate.pl?nick=$nick instead!"); + return; + } + my $person = $::sn{$nick}; + my $dbh = $::db->{DBH}; + + my $mnicks = $dbh->do("SELECT * from $::db->{ACTIONTABLE} WHERE nick like " . $dbh->quote($nick) . ';'); + my $musers = $dbh->do("SELECT * from $::db->{ACTIONTABLE} WHERE user like " . $dbh->quote($person->{user}) . ';'); + my $mhosts = $dbh->do("SELECT * from $::db->{ACTIONTABLE} WHERE host like " . $dbh->quote($person->{host}) . ';'); + my $maccts = $dbh->do("SELECT * from $::db->{ACTIONTABLE} WHERE account like " . $dbh->quote($person->{account}) . ';'); + my $mgecos = $dbh->do("SELECT * from $::db->{ACTIONTABLE} WHERE gecos like " . $dbh->quote($person->{gecos}) . ';'); + + my $ip = ASM::Util->getNickIP($nick); + my $matchedip = 0; + $matchedip = $dbh->do("SELECT * from $::db->{ACTIONTABLE} WHERE ip = " . $dbh->quote($ip) . ';') if defined($ip); + $conn->privmsg($event->replyto, "I found $mnicks matches by nick, $musers user matches, $mhosts by hostname, " . + "$maccts by NickServ account, $mgecos by gecos field, and $matchedip by real IP."); + ]]> + </command> + <command cmd="^;investigate2 (\S+) ?(\d*)$" flag="s"> + <![CDATA[ + my $nick = lc $1; + my $skip = 0; + $skip = $2 if (defined($2) and ($2 ne "")); + unless (defined($::sn{$nick})) { + $conn->privmsg($event->replyto, "I don't see $nick in my state tracking database, so I can't run any queries on their info, sorry :(" . + " You can try https://antispammeta.net/cgi-bin/secret/investigate.pl?nick=$nick instead!"); + return; + } + my $person = $::sn{$nick}; + my $dbh = $::db->{DBH}; + + my $query = "SELECT * from $::db->{ACTIONTABLE} WHERE nick like " . $dbh->quote($nick) . + ' or user like ' . $dbh->quote($person->{user}) . + ' or host like ' . $dbh->quote($person->{host}) . + ' or account like ' . $dbh->quote($person->{account}) . + ' or gecos like ' . $dbh->quote($person->{gecos}); + my $ip = ASM::Util->getNickIP($nick); + if (defined($ip)) { + $query = $query . ' or ip = ' . $dbh->quote($ip); + } + $query = $query . " order by time desc limit $skip,5;"; + print Dumper($query); + my $query_handle = $dbh->prepare($query); + $query_handle->execute(); + my @data = @{$query_handle->fetchall_arrayref()}; +# reverse @data; +#$data will be an array of arrays, + my ($xindex, $xtime, $xaction, $xreason, $xchannel, $xnick, $xuser, $xhost, $xip, $xgecos, $xaccount, $xbynick, $xbyuser, $xbyhost, $xbygecos, $xbyaccount ) = + ( 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + $conn->privmsg($event->replyto, "PM'ing you the list of results"); + foreach my $line (@data) { + my $reason = ''; + $reason = $line->[$xreason] if defined($line->[$xreason]); + $conn->privmsg($event->nick, '#' . $line->[$xindex] . ': ' . $line->[$xtime] . ' ' . + $line->[$xnick] . '!' . $line->[$xuser] . '@' . $line->[$xhost] . ' (' . $line->[$xgecos] . ') ' . + $line->[$xaction] . ' (' . $reason . ')' . + ' on ' . $line->[$xchannel] . ' by ' . $line->[$xbynick]); # . "\n"; + } + my $dq = ''; + if (defined($ip)) { + $dq = '&realip=' . join '.', unpack 'C4', pack 'N', $ip; + } + $conn->privmsg($event->nick, "Only 5 results are shown at a time. For more, do ;investigate2 $nick " . ($skip+5) . + ' or better yet, check out https://antispammeta.net/cgi-bin/secret/investigate.pl?nick=' . uri_escape($nick) . + '&user=' . uri_escape($person->{user}) . '&host=' . uri_escape($person->{host}) . '&account=' . uri_escape($person->{account}) . + '&gecos=' . uri_escape($person->{gecos}) . $dq ); +# print Dumper($data); + ]]> + </command> <command cmd="^;userx add (\S+) (\S+)$" flag="a"> <![CDATA[ my $acct = lc $1; my $flags = $2; + my %hasflagshash = (); + foreach my $item (split(//, $::users->{person}->{lc $::sn{lc $event->{nick}}->{account}}->{flags})) { + $hasflagshash{$item} = 1; + } + foreach my $flag (split(//, $flags)) { + if (!defined($hasflagshash{$flag})) { + $conn->privmsg($event->replyto, "You can't give a flag you don't already have."); + return; + } + } if ($flags =~ /d/i) { $conn->privmsg($event->replyto, "The d flag may not be assigned over IRC. Edit the configuration manually."); return; @@ -95,6 +230,16 @@ <![CDATA[ my $nick = lc $1; my $flags = $2; + my %hasflagshash = (); + foreach my $item (split(//, $::users->{person}->{lc $::sn{lc $event->{nick}}->{account}}->{flags})) { + $hasflagshash{$item} = 1; + } + foreach my $flag (split(//, $flags)) { + if (!defined($hasflagshash{$flag})) { + $conn->privmsg($event->replyto, "You can't give a flag you don't already have."); + return; + } + } if ($flags =~ /d/i) { $conn->privmsg($event->replyto, "The d flag may not be assigned over IRC. Edit the configuration manually."); return; diff --git a/config-default/mysql.xml b/config-default/mysql.xml index 3f66147..10aca77 100644 --- a/config-default/mysql.xml +++ b/config-default/mysql.xml @@ -3,6 +3,7 @@ <pass>PASS</pass> <db>asm_main</db> <table>alertlog</table> + <actiontable>actionlog</actiontable> <host>localhost</host> <port>3307</port> </mysql> diff --git a/config-default/settings.xml b/config-default/settings.xml index 66eab42..b128cac 100644 --- a/config-default/settings.xml +++ b/config-default/settings.xml @@ -21,6 +21,7 @@ </autojoins> <log> <dir>logs/</dir> + <actiondir>actionlogs/</actiondir> <filefmt>-%m-%d-%Y.log</filefmt> <timefmt>%B %d %T </timefmt> <zone>GMT</zone> diff --git a/config-default/users.xml b/config-default/users.xml index 7902bcc..ecf8de3 100644 --- a/config-default/users.xml +++ b/config-default/users.xml @@ -1,14 +1,18 @@ <people> - <person id="afterdeath" flags="hocdatp" /> + <person id="afterdeath" flags="hocdatps" /> + <person id="antispammeta" /> <person id="cbrown1023" flags="oath" /> <person id="dave2" flags="oath" /> <person id="denny" flags="ha" /> - <person id="dlange" flags="coathd" /> + <person id="dlange" flags="coathds" /> <person id="dmcdevit" flags="oath" /> <person id="dominian" flags="oath" /> <person id="dungodung" flags="oath" /> <person id="errantego" flags="doath" /> <person id="gary" flags="oath" /> + <person id="gry" /> + <person id="gryllida" flags="ahsto" /> + <person id="gwickwire" flags="s" /> <person id="idleone" flags="o" /> <person id="jeremyb" flags="oth" /> <person id="jonathand" flags="oath" /> @@ -18,28 +22,28 @@ <person id="logan_" flags="oath" /> <person id="lstarnes" flags="oath" /> <person id="marienz" flags="doath" /> - <person id="mrmist" flags="doath" /> <person id="martinp23" flags="oath" /> <person id="mquin" flags="hota" /> + <person id="mrmist" flags="doath" /> <person id="myrtti" flags="htoa" /> <person id="nhandler" flags="oath" /> <person id="njan" flags="oath" /> <person id="o_o" flags="oath" /> <person id="paradox" flags="h" /> - <person id="petersymonds" flags="oath" /> + <person id="petersymonds" flags="oaths" /> + <person id="pine" flags="ths" /> <person id="pricechild" flags="oath" /> <person id="richih" flags="oath" /> <person id="rjd0060" flags="th" /> - <person id="sauvin" flags="oath" /> + <person id="sauvin" flags="oaths" /> <person id="seanw" flags="oath" /> <person id="shiibot" flags="p" /> - <person id="snowolf" flags="oath" /> + <person id="snowolf" flags="oaths" /> + <person id="tbloemink" flags="ths" /> <person id="th1" flags="th" /> <person id="thehelpfulone" flags="oath" /> <person id="tomaw" flags="oathd" /> - <person id="troubled" flags="oath" /> - <person id="ttuttle" flags="oath" /> - <person id="werdan7" flags="oath" /> - <person id="wildpikachu" flags="doath" /> - <person id="windowshasyou" flags="oath" /> + <person id="ttuttle" flags="oaths" /> + <person id="werdan7" flags="oth" /> + <person id="wildpikachu" flags="doaths" /> </people> @@ -18,10 +18,13 @@ $Data::Dumper::Useqq=1; %::eline=(); $::pass = ''; +@::nick_blacklist=(); @::string_blacklist=(); $::netsplit = 0; $::debug = 0; $::cset = ''; +$::pacealerts = 1; +%::wordlist = (); ## debug variables. 0 to turn off debugging, else set it to a Term::ANSIColor constant. %::debugx = ( @@ -32,14 +35,16 @@ $::cset = ''; "chanstate" => MAGENTA, "restrictions" => BLUE, "startup" => YELLOW, - "mysql" => 0, + "mysql" => CYAN, "inspector" => 0, "commander" => GREEN, "msg" => GREEN, "dcc" => RED, - "misc" => 0, #RED + "misc" => 0, #RED, "latency" => RED, - "statsp" => 0 #MAGENTA + "statsp" => MAGENTA, + "ctcp" => 0, #RED, + "logger" => 0 ); %::dsock = (); %::spy = (); @@ -54,6 +59,13 @@ $SIG{__WARN__} = sub { print STDERR strftime("%F %T", gmtime), RED, ' WARNING: ', RESET, $_[0]; }; +sub alarmdeath +{ + die "SIG ALARM!!!\n"; +} +$SIG{ALRM} = \&alarmdeath; +alarm 300; + BEGIN { my @modules = qw/Util Xml Inspect Event Services Log Command Classes Mysql/; require 'modules/' . lc $_ . '.pl' foreach @modules; @@ -75,9 +87,12 @@ sub init { $host = ${$::settings->{server}}[rand @{$::settings->{server}}]; ASM::Util->dprint( "Connecting to $host", "startup"); $irc->debug($::debug); - $::db = ASM::DB->new($::mysql->{db}, $::mysql->{host}, $::mysql->{port}, $::mysql->{user}, $::mysql->{pass}, $::mysql->{table}, $::mysql->{dblog}); + $::db = ASM::DB->new($::mysql->{db}, $::mysql->{host}, $::mysql->{port}, + $::mysql->{user}, $::mysql->{pass}, $::mysql->{table}, + $::mysql->{actiontable}, $::mysql->{dblog}); $conn = $irc->newconn( Server => $host, Port => $::settings->{port} || '6667', + SSL => defined($::settings->{ssl}), Nick => $::settings->{nick}, Ircname => $::settings->{realname}, Username => $::settings->{username}, @@ -97,6 +112,9 @@ sub init { my @strbl = io('string_blacklist.txt')->getlines; chomp @strbl; @::string_blacklist = @strbl; + my @nickbl = io('nick_blacklist.txt')->getlines; + chomp @nickbl; + @::nick_blacklist = @nickbl; %::proxies = (); my @proxy = io('proxy.txt')->getlines; chomp @proxy; @@ -105,6 +123,11 @@ sub init { $::proxies{$1} = 1; } } + my @wl=io('wordlist.txt')->getlines; + chomp @wl; + foreach my $item (@wl) { + $::wordlist{lc $item} = 1; + } $irc->start(); } diff --git a/modules/classes.pl b/modules/classes.pl index f943976..fc8e9e7 100644 --- a/modules/classes.pl +++ b/modules/classes.pl @@ -26,13 +26,53 @@ sub new "nuhg" => \&nuhg, "levenflood" => \&levenflood, "proxy" => \&proxy, - "nickbl" => \&nickbl + "nickbl" => \&nickbl, + "nickfuzzy" => \&nickfuzzy, + "asciiflood" => \&asciiflood, + "joinmsgquit" => \&joinmsgquit, + "garbagemeter" => \&garbagemeter, + "cyclebotnet" => \&cyclebotnet }; $self->{ftbl} = $tbl; bless($self); return $self; } +sub garbagemeter { + my ($chk, $id, $event, $chan, $rev) = @_; + my @cut = split(/:/, $chk->{content}); + my $limit = int($cut[0]); + my $timeout = int($cut[1]); + my $threshold = int($cut[2]); + my $threshold2 = int($cut[3]); + my $wordcount = 0; + my $line = $event->{args}->[0]; + return 0 unless ($line =~ /^[A-Za-z: ]+$/); + my @words = split(/ /, $line); + return 0 unless ((scalar @words) >= $threshold2); + foreach my $word (@words) { + if (defined($::wordlist{lc $word})) { + $wordcount += 1; + } + return 0 if ($wordcount >= $threshold); + } + return 1 if ( flood_add( $chan, $id, 0, $timeout ) == $limit ); + return 0; +} + +sub joinmsgquit +{ + my ($chk, $id, $event, $chan, $rev) = @_; + my $time = $chk->{content}; +##STATE + $chan = lc $chan; #don't know if this is necessary but I'm trying to track down some mysterious state tracking corruption + return 0 unless defined($::sc{$chan}{users}{lc $event->{nick}}{jointime}); + return 0 unless defined($::sc{$chan}{users}{lc $event->{nick}}{msgtime}); + return 0 if ((time - $::sc{$chan}{users}{lc $event->{nick}}{jointime}) > $time); + return 0 if ((time - $::sc{$chan}{users}{lc $event->{nick}}{msgtime}) > $time); + return 1; +} + sub check { my $self = shift; @@ -40,6 +80,17 @@ sub check return $self->{ftbl}->{$item}->(@_); } +sub nickbl +{ + my ($chk, $id, $event, $chan, $rev) = @_; + my $match = lc $event->{nick}; + foreach my $line (@::nick_blacklist) { + if ($line eq $match) { + return 1; + } + } + return 0; +} sub proxy { my ($chk, $id, $event, $chan, $rev) = @_; @@ -85,7 +136,7 @@ sub levenflood return $ret; } -sub nickbl +sub nickfuzzy { my ($chk, $id, $event, $chan) = @_; my $nick = $event->{nick}; @@ -136,6 +187,26 @@ sub floodqueue { return 0; } +sub asciiflood { + my ($chk, $id, $event, $chan, $rev) = @_; + my @cut = split(/:/, $chk->{content}); + return 0 if (length($event->{args}->[0]) < $cut[0]); + return 0 if ($event->{args}->[0] =~ /[A-Za-z0-9]/); + return 1 if ( flood_add( $chan, $id, $event->{host}, int($cut[2]) ) == int($cut[1]) ); + return 0; +} + +sub cyclebotnet +{ + my ($chk, $id, $event, $chan, $rev) = @_; + my ($cycletime, $queueamt, $queuetime) = split(/:/, $chk->{content}); + $chan = lc $chan; #don't know if this is necessary but I'm trying to track down some mysterious state tracking corruption + return 0 unless defined($::sc{$chan}{users}{lc $event->{nick}}{jointime}); + return 0 if ((time - $::sc{$chan}{users}{lc $event->{nick}}{jointime}) > int($cycletime)); + return 1 if ( flood_add( $chan, $id, "cycle", int($queuetime)) == int($queueamt) ); + return 0; +} + sub nickspam { my ($chk, $id, $event, $chan) = @_; my @cut = split(/:/, $chk->{content}); diff --git a/modules/command.pl b/modules/command.pl index 3acaedd..7576850 100644 --- a/modules/command.pl +++ b/modules/command.pl @@ -5,6 +5,7 @@ use strict; use IO::All; use POSIX qw(strftime); use Data::Dumper; +use URI::Escape; sub new { diff --git a/modules/event.pl b/modules/event.pl index 3ed711c..bd76fb1 100644 --- a/modules/event.pl +++ b/modules/event.pl @@ -6,6 +6,7 @@ use Data::Dumper; use Text::LevenshteinXS qw(distance); use IO::All; use POSIX qw(strftime); +use Regexp::Wildcards; sub cs { my ($chan) = @_; @@ -21,12 +22,6 @@ sub maxlen { return $lb; } -sub alarmdeath -{ - die "SIG ALARM!!!\n"; -} -$SIG{ALRM} = \&alarmdeath; - sub new { my $module = shift; @@ -82,22 +77,24 @@ sub new my $clearstatsp = 1; my %statsp = (); +my %oldstatsp = (); sub on_statsdebug { my ($conn, $event) = @_; my ($char, $line) = ($event->{args}->[1], $event->{args}->[2]); if ($char eq 'p') { + if ($clearstatsp) { + $clearstatsp = 0; + %oldstatsp = %statsp; + %statsp = (); + } if ($line =~ /^(\d+) staff members$/) { #this is the end of the report } else { my ($nick, $userhost) = split(" ", $line); $userhost =~ s/\((.*)\)/$1/; my ($user, $host) = split("@", $userhost); - if ($clearstatsp) { - $clearstatsp = 0; - %statsp = (); - } $statsp{$nick}= [$user, $host]; } } @@ -109,24 +106,55 @@ sub on_endofstats if ($event->{args}->[1] eq 'p') { $clearstatsp=1; my $tmp = Dumper(\%statsp); chomp $tmp; - ASM::Util->dprint($tmp, 'statsp'); + if ( join(",", sort(keys %oldstatsp)) ne join(",", sort(keys %statsp)) ) { + open(FH, '>>', 'statsplog.txt'); + print FH strftime("%F %T ", gmtime) . join(",", sort(keys %statsp)) . "\n"; + close(FH); + ASM::Util->dprint(join(",", keys %statsp), 'statsp'); + } # $event->{args}->[2] == "End of /STATS report" #end of /stats p } } +my $lagcycles = 0; +my $pongcount = 0; + sub on_pong { my ($conn, $event) = @_; - alarm 90; - $conn->schedule( 60, sub { $conn->sl("PING :" . time); } ); + alarm 120; + $conn->schedule( 30, sub { $conn->sl("PING :" . time); } ); ASM::Util->dprint('Pong? ... Ping!', 'pingpong'); -# ASM::Util->dprint(Dumper($event), 'pingpong'); - $conn->sl("STATS p"); my $lag = time - $event->{args}->[0]; if ($lag > 1) { ASM::Util->dprint("Latency: $lag", 'latency'); } + if (($pongcount % 3) == 0) { #easiest way to do something roughly every 90 seconds + $conn->sl('STATS p'); + } + if ((time - $::starttime) < 60 ) { + return; #we don't worry about lag if we've just started up and are still syncing etc. + } + if (($lag > 2) && ($lag < 5)) { + $conn->privmsg( $::settings->{masterchan}, "Warning: I'm currently lagging by $lag seconds."); + } + if ($lag >= 5) { + $lagcycles++; + if ($lagcycles >= 3) { + $conn->quit("Automatic restart triggered due to persistent lag. Freenode staff: If this is happening too frequently, please " . + "set a nickserv freeze on my account, and once my connection is stable, unfreeze the account and /kill me to tri" . + "gger a reconnect."); + } elsif ($lagcycles >=2) { + $conn->privmsg( $::settings->{masterchan}, "Warning: I'm currently lagging by $lag seconds. This marks heavy lag cycle " . + "$lagcycles - automatic restart will be triggered after 3 lag cycles." ); + } + } + if (($lag <= 2) && ($lagcycles > 0)) { + $lagcycles--; +# $conn->privmsg( $::settings->{masterchan}, "Warning: Heavy lag cycle count has been reduced to $lagcycles" ); + ASM::Util->dprint('$lag = ' . $lag . '; $lagcycles = ' . $lagcycles, 'latency'); + } } sub on_dchat @@ -202,6 +230,8 @@ sub on_join { $::sc{$chan}{users}{$nick}{hostmask} = $event->{userhost}; $::sc{$chan}{users}{$nick}{op} = 0; $::sc{$chan}{users}{$nick}{voice} = 0; + $::sc{$chan}{users}{$nick}{jointime} = time; + $::sc{$chan}{users}{$nick}{msgtime} = 0; if (defined($::sn{$nick})) { my @mship = (); if (defined($::sn{$nick}->{mship})) { @@ -219,35 +249,43 @@ sub on_join { $::sn{$nick}->{user} = $event->{user}; $::sn{$nick}->{host} = $event->{host}; $::sn{$nick}->{account} = lc $event->{args}->[0]; - $::inspector->inspect( $conn, $event ) unless $::netsplit; $::db->logg($event); $::log->logg( $event ); + $::inspector->inspect( $conn, $event ) unless $::netsplit; } sub on_part { my ($conn, $event) = @_; - $::inspector->inspect( $conn, $event ); my $nick = lc $event->{nick}; + my $chan = lc $event->{to}->[0]; $::log->logg( $event ); $::db->logg( $event ); + if ($event->{args}->[0] =~ /^requested by/) { + my $idx = $::db->actionlog( $event); + $::log->sqlIncident($chan, $idx) if $idx; + } +# "to" => [ "#antispammeta" ], +# "args" => [ "requested by ow (test)" ], +# "nick" => "aoregcdu", + $::inspector->inspect( $conn, $event ); if (defined($::sn{$nick}) && defined($::sn{$nick}->{mship})) { my @mship = @{$::sn{$nick}->{mship}}; - @mship = grep { lc $_ ne lc $event->{to}->[0] } @mship; + @mship = grep { lc $_ ne $chan } @mship; if ( @mship ) { $::sn{$nick}->{mship} = \@mship; } else { delete($::sn{$nick}); } } - if ( lc $conn->{_nick} eq lc $nick ) + if ( lc $conn->{_nick} eq $nick ) { - delete( $::sc{lc $event->{to}->[0]} ); - on_byechan(lc $event->{to}->[0]); + delete( $::sc{$chan} ); + on_byechan($chan); } else { - delete( $::sc{lc $event->{to}->[0]}{users}{$nick} ); + delete( $::sc{$chan}{users}{$nick} ); } } @@ -265,19 +303,20 @@ sub on_public { my ($conn, $event) = @_; # alarm 200; - $::inspector->inspect( $conn, $event ); + $::sc{lc $event->{to}->[0]}{users}{lc $event->{nick}}{msgtime} = time; $::log->logg( $event ); $::db->logg( $event ); + $::inspector->inspect( $conn, $event ); $::commander->command( $conn, $event ); } sub on_notice { my ($conn, $event) = @_; - return if ( $event->{to}->[0] eq '$*' ); - $::inspector->inspect( $conn, $event ); + return if ( $event->{to}->[0] eq '$*' ); # if this is a global notice FUCK THAT SHIT $::log->logg( $event ); $::db->logg( $event ); + $::inspector->inspect( $conn, $event ); $::services->doServices($conn, $event); } @@ -302,17 +341,24 @@ sub on_quit my ($conn, $event) = @_; my @channels=(); for ( keys %::sc ) { - push ( @channels, $_ ) if delete $::sc{lc $_}{users}{lc $event->{nick}}; + push ( @channels, lc $_ ) if delete $::sc{lc $_}{users}{lc $event->{nick}}; } $event->{to} = \@channels; + my $idx = $::db->actionlog($event); + $::log->sqlIncident( join(',', @channels), $idx ) if $idx; $::db->logg( $event ); + $::log->logg( $event ); + if (($::netsplit == 0) && ($event->{args}->[0] eq "*.net *.split") && (lc $event->{nick} ne 'chanserv')) { #special, netsplit situation $conn->privmsg($::settings->{masterchan}, "Entering netsplit mode - JOIN and QUIT inspection will be disabled for 60 minutes"); $::netsplit = 1; $conn->schedule(60*60, sub { $::netsplit = 0; $conn->privmsg($::settings->{masterchan}, 'Returning to regular operation'); }); } $::inspector->inspect( $conn, $event ) unless $::netsplit; - $::log->logg( $event ); + #ugh. Repurge some shit, hopefully this will fix some stuff where things are going wrong + foreach my $chan ( keys %::sc ) { + delete $::sc{$chan}{users}{lc $event->{nick}}; + } delete($::sn{lc $event->{nick}}); } @@ -335,6 +381,7 @@ sub irc_users $::sc{lc $channel}{users}{lc $_} = {}; $::sc{lc $channel}{users}{lc $_}{op} = $op; $::sc{lc $channel}{users}{lc $_}{voice} = $voice; + $::sc{lc $channel}{users}{lc $_}{jointime} = 0; } } @@ -345,7 +392,6 @@ sub on_names { sub irc_topic { my ($conn, $event) = @_; - $::inspector->inspect($conn, $event) if ($event->{format} ne 'server'); if ($event->{format} eq 'server') { my $chan = lc $event->{args}->[1]; @@ -363,34 +409,42 @@ sub irc_topic { { if ($event->{type} eq 'topic') { - my $chan = lc $event->{args}->[1]; + my $chan = lc $event->{to}->[0]; $::sc{$chan}{topic}{text} = $event->{args}->[0]; $::sc{$chan}{topic}{time} = time; $::sc{$chan}{topic}{by} = $event->{from}; } $::log->logg($event); $::db->logg( $event ); + $::inspector->inspect($conn, $event); } } sub on_nick { my ($conn, $event) = @_; my @channels=(); - for ( keys %::sc ) + my $oldnick = lc $event->{nick}; + my $newnick = lc $event->{args}->[0]; + foreach my $chan ( keys %::sc ) { - if ( defined $::sc{lc $_}{users}{lc $event->{nick}} ) + $chan = lc $chan; + if ( defined $::sc{$chan}{users}{$oldnick} ) { - $::sc{lc $_}{users}{lc $event->{args}->[0]} = $::sc{lc $_}{users}{lc $event->{nick}}; - delete( $::sc{lc $_}{users}{lc $event->{nick}} ); - push ( @channels, lc $_ ); + if ($oldnick ne $newnick) { #otherwise a nick change where they're only + #changing the case of their nick means that + #ASM forgets about them. + $::sc{$chan}{users}{$newnick} = $::sc{$chan}{users}{$oldnick}; + delete( $::sc{$chan}{users}{$oldnick} ); + } + push ( @channels, $chan ); } } - $::sn{lc $event->{args}->[0]} = $::sn{lc $event->{nick}}; + $::sn{$newnick} = $::sn{$oldnick} if ($oldnick ne $newnick); $::db->logg( $event ); - delete( $::sn{lc $event->{nick}}); + delete( $::sn{$oldnick}) if ($oldnick ne $newnick); $event->{to} = \@channels; - $::inspector->inspect($conn, $event); $::log->logg($event); + $::inspector->inspect($conn, $event); } sub on_kick { @@ -399,18 +453,21 @@ sub on_kick { $conn->join($event->{args}->[0]); } my $nick = lc $event->{to}->[0]; + my $chan = lc $event->{args}->[0]; $::log->logg( $event ); $::db->logg( $event ); + my $idx = $::db->actionlog($event); + $::log->sqlIncident($chan, $idx) if $idx; if (defined($::sn{$nick}) && defined($::sn{$nick}->{mship})) { my @mship = @{$::sn{$nick}->{mship}}; - @mship = grep { lc $_ ne lc $event->{to}->[0] } @mship; + @mship = grep { lc $_ ne $chan } @mship; if ( @mship ) { $::sn{$nick}->{mship} = \@mship; } else { delete($::sn{$nick}); } } - if ( lc $conn->{_nick} eq lc $nick ) + if ( lc $conn->{_nick} eq $nick ) { delete( $::sc{lc $event->{args}->[0]} ); on_byechan(lc $event->{to}->[0]); @@ -433,14 +490,26 @@ sub parse_modes $t=$c; } else { #eIbq,k,flj,CFLMPQcgimnprstz - if ( grep( /[eIbqkfljov]/,($c) ) ) { #modes that take args - push (@new_modes, [$t.$c, shift @args]); - } - elsif ( grep( /[CFLMPQcgimnprstz]/, ($c) ) ) { - push (@new_modes, [$t.$c]); - } - else { - die "Unknown mode $c !\n"; + if ($t eq '+') { + if ( grep( /[eIbqkfljov]/,($c) ) ) { #modes that take args WHEN BEING ADDED + push (@new_modes, [$t.$c, shift @args]); + } + elsif ( grep( /[CFLMPQcgimnprstz]/, ($c) ) ) { + push (@new_modes, [$t.$c]); + } + else { + die "Unknown mode $c !\n"; + } + } else { + if ( grep( /[eIbqov]/,($c) ) ) { #modes that take args WHEN BEING REMOVED + push (@new_modes, [$t.$c, shift @args]); + } + elsif ( grep( /[CFLMPQcgimnprstzkflj]/, ($c) ) ) { + push (@new_modes, [$t.$c]); + } + else { + die "Unknown mode $c !\n"; + } } } } @@ -470,12 +539,45 @@ sub on_channelmodeis } } +sub whoGotHit +{ + my ($chan, $mask) = @_; + my $cvt = Regexp::Wildcards->new(type => 'jokers'); + my @affected = (); + if ($mask !~ /^\$/) { + my @div = split(/\$/, $mask); + my $regex = $cvt->convert($div[0]); + foreach my $nick (keys %::sn) { + next unless defined($::sn{$nick}{user}); + if (lc ($nick.'!'.$::sn{$nick}{user}.'@'.$::sn{$nick}{host}) =~ lc $regex) { + push @affected, $nick if defined($::sc{$chan}{users}{$nick}); + } + } + } elsif ($mask =~ /^\$a:(.*)/) { + my @div = split(/\$/, $1); + my $regex = $cvt->convert($div[0]); + foreach my $nick (keys %::sn) { + next unless defined($::sn{$nick}{account}); + if (lc ($::sn{$nick}{account}) =~ lc $regex) { + push @affected, $nick if defined($::sc{$chan}{users}{$nick}); + } + } + } + return @affected; +} + sub on_mode { my ($conn, $event) = @_; my $chan = lc $event->{to}->[0]; +# holy shit, I feel so bad doing this +# I have no idea how or why Net::IRC fucks up modes if they've got a ':' in one of the args +# but you do what you must... + my @splitted = split(/ /, $::lastline); shift @splitted; shift @splitted; shift @splitted; + $event->{args}=\@splitted; if ($chan =~ /^#/) { my @modes = @{parse_modes($event->{args})}; + ASM::Util->dprint(Dumper(\@modes), 'misc'); foreach my $line ( @modes ) { my @ex = @{$line}; @@ -484,23 +586,39 @@ sub on_mode elsif ( $ex[0] eq '+v' ) { $::sc{$chan}{users}{lc $ex[1]}{voice} = 1; } elsif ( $ex[0] eq '-v' ) { $::sc{$chan}{users}{lc $ex[1]}{voice} = 0; } - elsif ( $ex[0] eq '+b') { + elsif ( $ex[0] eq '+b' ) { $::sc{$chan}{bans}{$ex[1]} = { bannedBy => $event->{from}, bannedOn => time }; + if (lc $event->{nick} !~ /^(floodbot)/) { #ignore the ubuntu floodbots 'cause they quiet people a lot + my @affected = whoGotHit($chan, $ex[1]); + if ( (@affected) && (scalar @affected <= 4) ) { + foreach my $victim (@affected) { + my $idx = $::db->actionlog($event, 'ban', $victim); + $::log->sqlIncident( $chan, $idx ) if $idx; + } + } + } } - elsif ( $ex[0] eq '-b') { - delete $::sc{$chan}{bans}{$ex[1]}; - } - elsif ( $ex[0] eq '+q') { + elsif ( $ex[0] eq '-b' ) { delete $::sc{$chan}{bans}{$ex[1]}; } + + elsif ( $ex[0] eq '+q' ) { $::sc{$chan}{quiets}{$ex[1]} = { bannedBy => $event->{from}, bannedOn => time }; + if (lc $event->{nick} !~ /^(floodbot)/) { + my @affected = whoGotHit($chan, $ex[1]); + if ( (@affected) && (scalar @affected <= 4) ) { + foreach my $victim (@affected) { + my $idx = $::db->actionlog($event, 'quiet', $victim); + $::log->sqlIncident( $chan, $idx ) if $idx; + } + } + } } - elsif ( $ex[0] eq '-q') { - delete $::sc{$chan}{quiets}{$ex[1]}; - } + elsif ( $ex[0] eq '-q' ) { delete $::sc{$chan}{quiets}{$ex[1]}; } + else { my ($what, $mode) = split (//, $ex[0]); if ($what eq '+') { if (defined($ex[1])) { push @{$::sc{$chan}{modes}}, $mode . ' ' . $ex[1]; } - else { push @{$::sc{$chan}{modes}}, $mode; } + else { push @{$::sc{$chan}{modes}}, $mode; } } else { my @modes = grep {!/^$mode/} @{$::sc{$chan}{modes}}; $::sc{$chan}{modes} = \@modes; @@ -518,7 +636,7 @@ sub on_mode sub checkRegged { my ($conn, $chan) = @_; - if (grep {/r/} @{$::sc{$chan}{modes}}) { + if (grep {/^r/} @{$::sc{$chan}{modes}}) { my $tgt = $chan; my $risk = "debug"; my $hilite=ASM::Util->commaAndify(ASM::Util->getAlert($tgt, $risk, 'hilights')); @@ -553,6 +671,7 @@ sub on_ctcp { my ($conn, $event) = @_; my $acct = lc $::sn{lc $event->{nick}}->{account}; + ASM::Util->dprint(Dumper($event), 'ctcp'); if (($event->{type} eq 'cdcc') && (defined($::users->{person}->{$acct})) && (defined($::users->{person}->{$acct}->{flags})) && @@ -576,9 +695,7 @@ sub dcc_open sub on_ctcp_source { my ($conn, $event) = @_; - if (lc $event->{args}->[0] eq lc $conn->{_nick}) { - $conn->ctcp_reply($event->{nick}, 'SOURCE http://svn.linuxrulz.org/repos/antispammeta/trunk/'); - } + $conn->ctcp_reply($event->{nick}, 'SOURCE http://svn.linuxrulz.org/repos/antispammeta/trunk/'); } sub on_whoxreply @@ -631,6 +748,13 @@ sub on_whofuckedup { my ($conn, $event) = @_; ASM::Util->dprint('on_whofuckedup called!', 'sync'); + if ($event->{args}->[1] eq "STATS") { +#most likely this is getting called because we did stats p too often. +#unfortunately the server doesn't let us know what exactly we called stats for. +#anyways, we don't need to do anything for this + } else { #dunno why it got called, print the data and I'll add a handler for it. + ASM::Util->dprint(Dumper($event), 'sync'); + } } sub on_bannedfromchan { diff --git a/modules/inspect.pl b/modules/inspect.pl index 24d93d8..4466d69 100644 --- a/modules/inspect.pl +++ b/modules/inspect.pl @@ -31,7 +31,7 @@ sub inspect { } } else { - $iaddr = gethostbyname($event->{host}); + $iaddr = gethostbyname($event->{host}) if ($event->{host} !~ /\//); $rev = join('.', reverse(unpack('C4', $iaddr))).'.' if (defined $iaddr); } ## NB: isn't there a better way to do this with grep, somehow? @@ -74,12 +74,16 @@ sub inspect { if ($id eq 'last_measure_regex') { #TODO: Note that this is another example of things that shouldn't be hardcoded, but are. } - unless (defined($::ignored{$chan}) && ($::ignored{$chan} >= $::RISKS{$dct{$id}{risk}})) { + if ( + (!(defined($::ignored{$chan}) && ($::ignored{$chan} >= $::RISKS{$dct{$id}{risk}}))) || + (($::pacealerts == 0) && ($dct{$id}{risk} eq 'info')) + ) { my @tgts = ASM::Util->getAlert($chan, $dct{$id}{risk}, 'msgs'); ASM::Util->sendLongMsg($conn, \@tgts, $txtz); $::ignored{$chan} = $::RISKS{$dct{$id}{risk}}; $conn->schedule(45, sub { delete($::ignored{$chan})}); } + $::log->incident($chan, "$chan: $dct{$id}{risk} risk: $event->{nick} - $nicereason\n"); delete $dct{$id}{xresult}; } } diff --git a/modules/log.pl b/modules/log.pl index 8b30eab..bdf4821 100644 --- a/modules/log.pl +++ b/modules/log.pl @@ -12,10 +12,43 @@ sub new my $config = shift; my $self = {}; $self->{CONFIG} = $config; + $self->{backlog} = {}; bless($self); return $self; } +sub incident +{ + my $self = shift; + my ($chan, $header) = @_; + $chan = lc $chan; + open(FH, '>>', 'dctlog.txt'); + print FH $header; + if (defined($self->{backlog}->{$chan})) { + print FH join('', @{$self->{backlog}->{$chan}}); + } + print FH "\n\n"; + close(FH); +} + +#writes out the backlog to a file which correlates to ASM's SQL actionlog table +sub sqlIncident +{ + my $self = shift; + my ($channel, $index) = @_; + $channel = lc $channel; + my @chans = split(/,/, $channel); + open(FH, '>', $self->{CONFIG}->{actiondir} . $index . '.txt'); + foreach my $chan (@chans) { + if (defined($self->{backlog}->{$chan})) { + print FH "$chan\n"; + print FH join('', @{$self->{backlog}->{$chan}}); + print FH "\n"; + } + } + close(FH); +} + sub logg { my $self = shift; @@ -35,10 +68,10 @@ sub logg $_ = ''; $_ = "<$event->{nick}> $event->{args}->[0]" if $event->{type} eq 'public'; $_ = "*** $event->{nick} has joined $chan" if $event->{type} eq 'join'; - $_ = "*** $event->{nick} has left $chan" if $event->{type} eq 'part'; + $_ = "*** $event->{nick} has left $chan ($event->{args}->[0])" if $event->{type} eq 'part'; $_ = "* $event->{nick} $event->{args}->[0]" if $event->{type} eq 'caction'; $_ = "*** $event->{nick} is now known as $event->{args}->[0]" if $event->{type} eq 'nick'; - $_ = "*** $event->{nick} has quit IRC" if $event->{type} eq 'quit'; + $_ = "*** $event->{nick} has quit ($event->{args}->[0])" if $event->{type} eq 'quit'; $_ = "*** $event->{to}->[0] was kicked by $event->{nick}" if $event->{type} eq 'kick'; $_ = "-$event->{nick}- $event->{args}->[0]" if $event->{type} eq 'notice'; $_ = "*** $event->{nick} sets mode: " . join(" ",@{$event->{args}}) if $event->{type} eq 'mode'; @@ -46,8 +79,18 @@ sub logg my $nostamp = $_; $_ = strftime($cfg->{timefmt}, @time) . $_ . "\n"; my $line = $_; + my @backlog = (); + if (defined($self->{backlog}->{$chan})) { + @backlog = @{$self->{backlog}->{$chan}}; + if (scalar @backlog >= 30) { + shift @backlog; + } + } + push @backlog, $line; + $self->{backlog}->{$chan} = \@backlog; if (open(FH, $path)) { # or die "Can't open $path: $!"; print FH $line; + ASM::Util->dprint($line, 'logger'); close(FH); } else { print "COULDN'T PRINT TO $path - $line"; diff --git a/modules/mysql.pl b/modules/mysql.pl index d0cee00..7484ca3 100644 --- a/modules/mysql.pl +++ b/modules/mysql.pl @@ -6,13 +6,14 @@ use DBI; sub new { my $module = shift; - my ($db, $host, $port, $user, $pass, $table, $dblog) = @_; + my ($db, $host, $port, $user, $pass, $table, $actiontable, $dblog) = @_; my $self = {}; $self->{DBH} = DBI->connect("DBI:mysql:database=$db;host=$host;port=$port", $user, $pass); $self->{DBH_LOG} = DBI->connect("DBI:mysql:database=$dblog;host=$host;port=$port", $user, $pass); $self->{DBH}->{mysql_auto_reconnect} = 1; $self->{DBH_LOG}->{mysql_auto_reconnect} = 1; $self->{TABLE} = $table; + $self->{ACTIONTABLE} = $actiontable; bless($self); return $self; } @@ -60,6 +61,122 @@ sub record $dbh->quote($id) . ", " . $dbh->quote($reason) . ");"); } +sub actionlog +{ + my ($self, $event, $modedata1, $modedata2) = @_; + my $dbh = $self->{DBH}; + my ($action, $reason, $channel, + $nick, $user, $host, $gecos, $account, $ip, + $bynick, $byuser, $byhost, $bygecos, $byaccount); + + if ($event->{type} eq 'mode') { + $action = $modedata1; + $nick = $modedata2; + $channel = lc $event->{to}->[0]; + $bynick = $event->{nick}; + $byuser = $event->{user}; + $byhost = $event->{host}; + } elsif ($event->{type} eq 'quit') { + my $quitmsg = $event->{args}->[0]; + if ($quitmsg =~ /^Killed \((\S+) \((.*)\)\)$/) { + $bynick = $1; + $reason = $2 unless ($2 eq '<No reason given>'); + return if ($reason =~ /Nickname regained by services/); + $action = 'kill'; + } elsif ($quitmsg =~ /^K-Lined$/) { + $action = 'k-line'; + } else { + return; #quit not forced/tracked + } + $nick = $event->{nick}; + $user = $event->{user}; + $host = $event->{host}; + } elsif (($event->{type} eq 'part') && ($event->{args}->[0] =~ /^requested by (\S+) \((.*)\)/)) { + $bynick = $1; + $reason = $2 unless (lc $reason eq lc $event->{nick}); + $action = 'remove'; + $nick = $event->{nick}; + $user = $event->{user}; + $host = $event->{host}; + $channel = $event->{to}->[0]; + } elsif ($event->{type} eq 'kick') { + $action = 'kick'; + $bynick = $event->{nick}; + $byuser = $event->{user}; + $byhost = $event->{host}; + $reason = $event->{args}->[1] unless ($event->{args}->[1] eq $event->{to}->[0]); + $nick = $event->{to}->[0]; + $channel = $event->{args}->[0]; + } + return unless defined($action); +# $bynick = lc $bynick if defined $bynick; #we will lowercase the NUHGA info later. + if ( (defined($bynick)) && (defined($::sn{lc $bynick})) ) { #we have the nick taking the action available, fill in missing NUHGA info + $byuser = $::sn{lc $bynick}{user} unless defined($byuser); + $byhost = $::sn{lc $bynick}{host} unless defined($byhost); + $bygecos = $::sn{lc $bynick}{gecos} unless defined($bygecos); + $byaccount = $::sn{lc $bynick}{account} unless defined($byaccount); + if (($byaccount eq '0') or ($byaccount eq '*')) { + $byaccount = undef; + } + } +# $nick = lc $nick if defined $nick; + if ( (defined($nick)) && (defined($::sn{lc $nick})) ) { #this should always be true, else something has gone FUBAR + $user = $::sn{lc $nick}{user} unless defined($user); + $host = $::sn{lc $nick}{host} unless defined($host); + $gecos = $::sn{lc $nick}{gecos} unless defined($gecos); + $account = $::sn{lc $nick}{account} unless defined($account); + if (($account eq '0') or ($account eq '*')) { + $account = undef; + } + $ip = ASM::Util->getNickIP(lc $nick); + } +# my ($action, $reason, $channel, +# $nick, $user, $host, $gecos, $account, $ip +# $bynick, $byuser, $byhost, $bygecos, $byaccount); +#Now, time to escape/NULLify everything + $action = $dbh->quote($action); + if (defined($reason)) { $reason = $dbh->quote($reason); } else { $reason = 'NULL'; } +## removed lc's from everything except IP + if (defined($channel)) { $channel = $dbh->quote($channel); } else { $channel = 'NULL'; } + + if (defined($nick)) { $nick = $dbh->quote($nick); } else { $nick = 'NULL'; } + if (defined($user)) { $user = $dbh->quote($user); } else { $user = 'NULL'; } + if (defined($host)) { $host = $dbh->quote($host); } else { $host = 'NULL'; } + if (defined($gecos)) { $gecos = $dbh->quote($gecos); } else { $gecos = 'NULL'; } + if (defined($account)) { $account = $dbh->quote($account); } else { $account = 'NULL'; } + if (defined($ip)) { $ip = $dbh->quote($ip); } else { $ip = 'NULL'; } + + if (defined($bynick)) { $bynick = $dbh->quote($bynick); } else { $bynick = 'NULL'; } + if (defined($byuser)) { $byuser = $dbh->quote($byuser); } else { $byuser = 'NULL'; } + if (defined($byhost)) { $byhost = $dbh->quote($byhost); } else { $byhost = 'NULL'; } + if (defined($bygecos)) { $bygecos = $dbh->quote($bygecos); } else { $bygecos = 'NULL'; } + if (defined($byaccount)) { $byaccount = $dbh->quote($byaccount); } else { $byaccount = 'NULL'; } + my $sqlstr = "INSERT INTO $self->{ACTIONTABLE} " . + "(action, reason, channel, " . + "nick, user, host, gecos, account, ip, " . + "bynick, byuser, byhost, bygecos, byaccount)" . + " VALUES " . + "($action, $reason, $channel, " . + "$nick, $user, $host, $gecos, $account, $ip, " . + "$bynick, $byuser, $byhost, $bygecos, $byaccount);"; + ASM::Util->dprint( $sqlstr, 'mysql' ); + $dbh->do( $sqlstr ); + return $dbh->last_insert_id(undef, undef, $self->{ACTIONTABLE}, undef); +# $::sn{ow} looks like: +#$VAR1 = { +# "account" => "afterdeath", +# "gecos" => "William Athanasius Heimbigner", +# "user" => "icxcnika", +# "mship" => [ +# "#baadf00d", +# "#antispammeta-debug", +# "#antispammeta" +# ], +# "host" => "freenode/weird-exception/network-troll/afterdeath" +# }; + +} + #FIXME: This function is shit. Also, it doesn't work like I want it to with mode. sub logg { @@ -110,7 +227,7 @@ sub logg $string = $string . ', ' . $dbh->quote($event->{args}->[1]); } $string = $string . ');'; - ASM::Util->dprint($string, "mysql"); +# ASM::Util->dprint($string, "mysql"); $dbh->do($string); } diff --git a/modules/util.pl b/modules/util.pl index dc3f285..54e25a6 100644 --- a/modules/util.pl +++ b/modules/util.pl @@ -183,6 +183,47 @@ sub dprint { print STDERR $text, "\n"; } +sub dottedQuadToInt +{ + my ($module, $dottedquad) = @_; + my $ip_number = 0; + my @octets = split(/\./, $dottedquad); + foreach my $octet (@octets) { + $ip_number <<= 8; + $ip_number |= $octet; + } + return $ip_number; +} + +sub getNickIP +{ + my ($module, $nick) = @_; + $nick = lc $nick; + return unless defined($::sn{$nick}); + if (defined($::sn{$nick}{ip})) { + return $::sn{$nick}{ip}; + } + my $host = $::sn{$nick}{host}; + if ( ($host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) or + ($host =~ /^gateway\/web\/freenode\/ip\.(\d+)\.(\d+)\.(\d+)\.(\d+)$/) ) { + #yay, easy IP! + $::sn{$nick}{ip} = dottedQuadToInt(undef, "$1.$2.$3.$4"); + return $::sn{$nick}{ip}; + } elsif (index($host, '/') != -1) { + return; + } elsif ($host =~ /^2001:0:/) { + my @splitip = split(/:/, $host); + #I think I can just do (hex($splitip[6] . $splitip[7]) ^ hex('ffffffff')) here but meh + my $host = join('.', unpack('C4', pack('N', (hex($splitip[6] . $splitip[7])^hex('ffffffff'))))); + $::sn{$nick}{ip} = dottedQuadToInt(undef, $host); + return $::sn{$nick}{ip}; + } + my @resolve = gethostbyname($::sn{$nick}{host}); + return unless @resolve; + $::sn{$nick}{ip} = dottedQuadToInt(undef, join('.', unpack('C4', $resolve[4]))); + return $::sn{$nick}{ip}; +} + sub notRestricted { my ($module, $nick, $restriction) = @_; $nick = lc $nick; |
