package My::parser::dovecot_parser; use strict; use warnings; sub new { my $class = shift; my $self = {}; bless ($self, $class); return $self; } sub parser { my $self = shift; my $string = shift; my ($reply,$hostile,$host) = ("No match for $string",0,''); my $re_host = qr/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/; my $re_usr = qr/[a-zA-Z0-9_-]*/; my $re_pid = qr/[0-9]*/; my $re_uid = qr/[a-zA-Z0-9_\-+:\/]*/; my $re_info = qr/\($re_usr\)\<$re_pid\>\<$re_uid\>/; if($string =~ m/ Info: Disconnected: Logged out /) { $reply = 'Normal logout'; } elsif($string =~ m/ imap-login: (Info: |)Login: /) { $reply = 'Normal login'; } elsif($string =~ m/ indexer-worker$re_info: Info: /) { $reply = 'Indexer worker message'; } elsif($string =~ m/ Disconnected (in IDLE|for inactivity) /) { $reply = 'Idle disconnect'; } elsif($string =~ m/imap($re_info): (Info: |Disconnected: |)Connection closed/) { $reply = 'Normal connection closed'; } elsif($string =~ m/ imap$re_info: (Info: |)Disconnected: Logged out /) { $reply = 'Normal log out'; } elsif($string =~ m/ master: Warning: Time moved /) { $reply = 'Clock adjustment'; } elsif($string =~ m/auth failed/) { $_ = $string; $reply = 'Auth failure'; $hostile = 1; PARSER: m/ (rip=($re_host)) /gcix && do { $host = $2; }; } elsif($string =~ m/(Authentication error (\(Password mismatch\?\)|unknown user))/) { $_ = $string; $reply = 'Unknown user or auth error'; $hostile = 1; PARSER: m/(([a-zA-Z0-9@._-]*),($re_host),.*\): pam_authenticate\(\) failed: (Authentication error \(Password mismatch\?\)|unknown user))/gi && do { $host = $3; }; } elsif($string =~ m/unknown user/) { $_ = $string; $reply = 'Unknown user'; $hostile = 1; PARSER: m/(((Info: |)conn unix:auth-worker \(uid=([0-9]{1,9})\): auth-worker<([0-9]{1,9}>: pam\(([a-zA-Z0-9@._-]*),($re_host),<($re_uid)>\): unknown user)))/gi && do { $host = $7; }; } elsif($string =~ m/ imap-login: (Info: |)Disconnected/) { if($string =~ m/Connection closed/) { $hostile = 0; $reply = 'Disconnecting is legit'; } elsif($string =~ m/ TLS handshaking: /) { $reply = 'TLS error'; $hostile = 1; } elsif($string =~ m/Too many invalid commands/) { $reply = 'Too many invalid commands'; $hostile = 1; } elsif($string =~ m/TLS: Disconnected,/) { $reply = 'Likely a sleeping android'; $hostile = 0; } elsif($string =~ m/client didn't finish SASL auth/) { $reply = 'Timeout waiting for SASL auth'; $hostile = 1; } elsif($string =~ m/no auth attempts in/) { if($string =~ m/, secured/) { $reply = 'Secured Disconnect during auth, either sleeping phone or attack on webmail'; $hostile = 0; } else { $reply = 'Non-secure disconnect during auth'; $hostile = 1; } } if($hostile) { $_ = $string; PARSER: m/\ (rip=($re_host))/gcix && do { $host = $2; }; } } elsif($string =~ m/ imap-login: (Info: |) Aborted login /) { $_ = $string; $reply = 'Aborted login'; $hostile = 1; PARSER: m/ (rip=($re_host)) /gcix && do { $host = $2; }; } elsif($string =~ m/unknown user$/) { $_ = $string; $hostile = 1; $reply = 'Unknown user'; PARSER: m/Info: pam\([a-zA-Z0-9@._-]*,($re_host),\<.*\>\): unknown user/gi && do { $host = $1; }; } else { $reply = 'No match for '.$string; } return { retval => 1, retmsg => $reply, hostile => $hostile, host => $host, string => $string }; } 1;