From 709a398708ae17c0e16fff4be5ac28432d4c7f07 Mon Sep 17 00:00:00 2001 From: Gilles Darold Date: Sun, 17 Jun 2018 23:47:16 +0200 Subject: [PATCH] Add parsing of SPF/DKIM log entries. Thanks to r-sherwood for the feature request. --- Makefile.PL | 5 +- debian/sendmailanalyzer.conf | 4 + sa_cache | 236 ++++++++++++++++++++++++++++++++++- sendmailanalyzer | 88 ++++++++++++- 4 files changed, 325 insertions(+), 8 deletions(-) diff --git a/Makefile.PL b/Makefile.PL index 2505ef0..89e32d4 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -240,7 +240,6 @@ ERROR_CODE lang/ERROR_CODE # sender or recipient relay to determine the email direction. #VIRTUAL_DOMAIN_DB_QUERY SELECT name FROM virtual_domains - # Syslog name of MimeDefang. Syslog write it to maillog with the pid as follow: # ... mimedefang.pl[1234] ... This is required to only parse relevant logged lines # Based on parsing mimedefang log generated by method md_graphdefang_log() @@ -304,6 +303,10 @@ CLAMSMTPD_NAME clamsmtpd # Can be overwritten with --postgrey or -g POSTGREY_NAME postgrey|sqlgrey +# Syslog name of SPF and DKIM log entry. Syslog write it to maillog with the +# pid as follow: ... opendmarc[1234] ... +SPF_DKIM_NAME opendmarc|opendkim + # HTML charset to use. Default is iso-8859-1, but with cyrillics you may want # to use utf-8 instead. #HTML_CHARSET utf-8 diff --git a/debian/sendmailanalyzer.conf b/debian/sendmailanalyzer.conf index 85d35bf..fe939c0 100644 --- a/debian/sendmailanalyzer.conf +++ b/debian/sendmailanalyzer.conf @@ -256,6 +256,10 @@ CLAMSMTPD_NAME clamsmtpd # Can be overwritten with --postgrey or -g POSTGREY_NAME postgrey|sqlgrey +# Syslog name of SPF and DKIM log entry. Syslog write it to maillog with the +# pid as follow: ... opendmarc[1234] ... +SPF_DKIM_NAME opendmarc|opendkim + # HTML charset to use. Default is iso-8859-1, but with cyrillics you may want # to use utf-8 instead. #HTML_CHARSET utf-8 diff --git a/sa_cache b/sa_cache index b7e5e4c..e7c8433 100755 --- a/sa_cache +++ b/sa_cache @@ -278,6 +278,7 @@ our %topauth = (); our %postgrey = (); our %toppostgrey = (); our %starttls = (); +our %spf_dkim = (); #### @@ -634,6 +635,27 @@ sub do_hour_cache close(IN); } + $file = "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day/spf_dkim.dat"; + if (open(IN, $file)) { + while (my $l = ) { + chomp($l); + # Format: Hour:Id:Type:Rule:Domain:Status + @data = split(/:/, $l); + $data[0] =~ /^(\d{2})/; + next if ($1 ne $hour); + if ($data[2] eq 'spf') { + $localstat{$data[1]}{$data[2]}{$data[3]}{hour} = $data[0] if (!exists $localstat{$data[1]}{$data[2]}{$data[3]}{hour}); + #$localstat{$data[1]}{$data[2]}{$data[3]}{domain}{$data[4]}++; + $localstat{$data[1]}{$data[2]}{$data[3]}{status}{$data[5]}++; + } elsif ($data[2] eq 'dkim') { + $localstat{$data[1]}{$data[2]}{hour} = $data[0] if (!exists $localstat{$data[1]}{$data[2]}{hour}); + #$localstat{$data[1]}{$data[2]}{domain}{$data[4]}++; + $localstat{$data[1]}{$data[2]}{status}{$data[5]}++; + } + } + close(IN); + } + for my $typ (sort keys %ANTISPAM_NAME) { $file = "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day/$typ.dat"; if (open(IN, $file)) { @@ -816,8 +838,6 @@ sub compute_top_stats $toppostgrey{rcpt}{$STATS->{$id}{rcpt}[$i]}++; } $toppostgrey{reason}{$STATS->{$id}{reason}}++; - - } if (exists $STATS->{$id}{spamtype} && grep(/^$STATS->{$id}{spamtype}$/, keys %ANTISPAM_NAME)) { @@ -1475,6 +1495,25 @@ sub compute_global_stats $postgrey{reason}{$STATS->{$id}{reason}}++; } + if (exists $STATS->{$id}{spf}) { + foreach my $r (keys %{ $STATS->{$id}{spf} }) { + #foreach my $k (keys %{ $STATS->{$id}{spf}{$r}{domain} }) { + # $spf_dkim{spf}{$r}{domain}{$k} += $STATS->{$id}{spf}{$r}{domain}{$k}; + #} + foreach my $k (keys %{ $STATS->{$id}{spf}{$r}{status} }) { + $spf_dkim{spf}{$r}{status}{$k} += $STATS->{$id}{spf}{$r}{status}{$k}; + } + } + } + if (exists $STATS->{$id}{dkim}) { + foreach my $k (keys %{ $STATS->{$id}{dkim}{status} }) { + $spf_dkim{dkim}{status}{$k} += $STATS->{$id}{dkim}{status}{$k}; + } + #foreach my $k (keys %{ $STATS->{$id}{dkim}{domain} }) { + # $spf_dkim{dkim}{domain}{$k} += $STATS->{$id}{dkim}{domain}{$k}; + #} + } + } # Top STARTTLS stats @@ -1637,6 +1676,32 @@ sub compute_global_stats print OUT "},\n"; print OUT ");\n\n"; %postgrey = (); + + print OUT "\%::spf_dkim = (\n"; + print OUT "'spf' => {\n"; + foreach my $r (keys %{$spf_dkim{spf}}) { + print OUT "\t'$r' => {\n"; + foreach my $s (keys %{$spf_dkim{spf}{$r}}) { + print OUT "\t\t'$s' => { "; + foreach my $v (keys %{$spf_dkim{spf}{$r}{$s}}) { + print OUT "'$v' => '$spf_dkim{spf}{$r}{$s}{$v}',"; + } + print OUT " },\n"; + } + print OUT "\t},\n"; + } + print OUT "},\n"; + print OUT "'dkim' => {\n"; + foreach my $r (keys %{$spf_dkim{dkim}}) { + print OUT "\t'$r' => {\n"; + foreach my $s (keys %{$spf_dkim{dkim}{$r}}) { + print OUT "\t\t'$s' => '$spf_dkim{dkim}{$r}{$s}',\n"; + } + print OUT "\t},\n"; + } + print OUT "},\n"; + print OUT ");\n\n"; + %spf_dkim = (); # Viruses flows / Viruses delivery flows / syserr flows print OUT "\%::virus = (\n"; @@ -1831,6 +1896,7 @@ sub do_day_cache my %localpostgrey = (); my %localtoppostgrey = (); my %localstarttls = (); + my %localspf_dkim = (); my $lbls = ''; my %authval = (); foreach my $hour ("00" .. "23") { @@ -1899,6 +1965,20 @@ sub do_day_cache $localpostgrey{reason}{$k} += $postgrey{reason}{$k}; } %postgrey = (); + + foreach my $r (keys %{$spf_dkim{spf}}) { + foreach my $k (keys %{$spf_dkim{spf}{$r}{status}}) { + $localspf_dkim{spf}{$r}{status}{$k} += $spf_dkim{spf}{$r}{status}{$k}; + } + foreach my $k (keys %{$spf_dkim{spf}{$r}{domain}}) { + $localspf_dkim{spf}{$r}{domain}{$k} += $spf_dkim{spf}{$r}{domain}{$k}; + } + } + foreach my $r (keys %{$spf_dkim{dkim}{status}}) { + $localspf_dkim{dkim}{status}{$r} += $spf_dkim{dkim}{status}{$r}; + } + %spf_dkim = (); + foreach my $k (keys %virus) { if (!grep(/^$k$/, 'lbls','x_label','values')) { $localvirus{$k} += $virus{$k}; @@ -2114,6 +2194,32 @@ sub do_day_cache print OUT ");\n\n"; %localpostgrey = (); + print OUT "\%::spf_dkim = (\n"; + print OUT "'spf' => {\n"; + foreach my $r (keys %{$localspf_dkim{spf}}) { + print OUT "\t'$r' => {\n"; + foreach my $s (keys %{$localspf_dkim{spf}{$r}}) { + print OUT "\t\t'$s' => { "; + foreach my $v (keys %{$localspf_dkim{spf}{$r}{$s}}) { + print OUT "'$v' => '$localspf_dkim{spf}{$r}{$s}{$v}',"; + } + print OUT " },\n"; + } + print OUT "\t},\n"; + } + print OUT "},\n"; + print OUT "'dkim' => {\n"; + foreach my $r (keys %{$localspf_dkim{dkim}}) { + print OUT "\t'$r' => {\n"; + foreach my $s (keys %{$localspf_dkim{dkim}{$r}}) { + print OUT "\t\t'$s' => '$localspf_dkim{dkim}{$r}{$s}',\n"; + } + print OUT "\t},\n"; + } + print OUT "},\n"; + print OUT ");\n\n"; + %localspf_dkim = (); + print OUT "\%::virus = (\n"; print OUT "'inbound' => '$localvirus{inbound}',\n"; print OUT "'inbound_bytes' => '$localvirus{inbound_bytes}',\n"; @@ -2644,6 +2750,7 @@ sub do_month_cache my %localpostgrey = (); my %localtoppostgrey = (); my %localstarttls = (); + my %localspf_dkim = (); my $lbls = ''; foreach my $day ("01" .. "31") { $lbls .= "$day:"; @@ -2712,6 +2819,20 @@ sub do_month_cache $localpostgrey{reason}{$k} += $postgrey{reason}{$k}; } %postgrey = (); + + foreach my $r (keys %{$spf_dkim{spf}}) { + foreach my $k (keys %{$spf_dkim{spf}{$r}{status}}) { + $localspf_dkim{spf}{$r}{status}{$k} += $spf_dkim{spf}{$r}{status}{$k}; + } + foreach my $k (keys %{$spf_dkim{spf}{$r}{domain}}) { + $localspf_dkim{spf}{$r}{domain}{$k} += $spf_dkim{spf}{$r}{domain}{$k}; + } + } + foreach my $r (keys %{$spf_dkim{dkim}{status}}) { + $localspf_dkim{dkim}{status}{$r} += $spf_dkim{dkim}{status}{$r}; + } + %spf_dkim = (); + foreach my $k (keys %virus) { if (!grep(/^$k$/, 'lbls','x_label','values')) { $localvirus{$k} += $virus{$k}; @@ -2928,6 +3049,32 @@ sub do_month_cache print OUT "},\n"; print OUT ");\n\n"; %localpostgrey = (); + + print OUT "\%::spf_dkim = (\n"; + print OUT "'spf' => {\n"; + foreach my $r (keys %{$localspf_dkim{spf}}) { + print OUT "\t'$r' => {\n"; + foreach my $s (keys %{$localspf_dkim{spf}{$r}}) { + print OUT "\t\t'$s' => { "; + foreach my $v (keys %{$localspf_dkim{spf}{$r}{$s}}) { + print OUT "'$v' => '$localspf_dkim{spf}{$r}{$s}{$v}',"; + } + print OUT " },\n"; + } + print OUT "\t},\n"; + } + print OUT "},\n"; + print OUT "'dkim' => {\n"; + foreach my $r (keys %{$localspf_dkim{dkim}}) { + print OUT "\t'$r' => {\n"; + foreach my $s (keys %{$localspf_dkim{dkim}{$r}}) { + print OUT "\t\t'$s' => '$localspf_dkim{dkim}{$r}{$s}',\n"; + } + print OUT "\t},\n"; + } + print OUT "},\n"; + print OUT ");\n\n"; + %localspf_dkim = (); print OUT "\%::virus = (\n"; print OUT "'inbound' => '$localvirus{inbound}',\n"; @@ -3452,6 +3599,7 @@ sub do_year_cache my %localpostgrey = (); my %localtoppostgrey = (); my %localstarttls = (); + my %localspf_dkim = (); my $lbls = ''; foreach my $month ("01" .. "12") { @@ -3520,6 +3668,20 @@ sub do_year_cache $localpostgrey{reason}{$k} += $postgrey{reason}{$k}; } %postgrey = (); + + foreach my $r (keys %{$spf_dkim{spf}}) { + foreach my $k (keys %{$spf_dkim{spf}{$r}{status}}) { + $localspf_dkim{spf}{$r}{status}{$k} += $spf_dkim{spf}{$r}{status}{$k}; + } + foreach my $k (keys %{$spf_dkim{spf}{$r}{domain}}) { + $localspf_dkim{spf}{$r}{domain}{$k} += $spf_dkim{spf}{$r}{domain}{$k}; + } + } + foreach my $r (keys %{$spf_dkim{dkim}{status}}) { + $localspf_dkim{dkim}{status}{$r} += $spf_dkim{dkim}{status}{$r}; + } + %spf_dkim = (); + foreach my $k (keys %virus) { if (!grep(/^$k$/, 'lbls','x_label','values')) { $localvirus{$k} += $virus{$k}; @@ -3739,7 +3901,33 @@ sub do_year_cache print OUT "},\n"; print OUT ");\n\n"; %localpostgrey = (); - + + print OUT "\%::spf_dkim = (\n"; + print OUT "'spf' => {\n"; + foreach my $r (keys %{$localspf_dkim{spf}}) { + print OUT "\t'$r' => {\n"; + foreach my $s (keys %{$localspf_dkim{spf}{$r}}) { + print OUT "\t\t'$s' => { "; + foreach my $v (keys %{$localspf_dkim{spf}{$r}{$s}}) { + print OUT "'$v' => '$localspf_dkim{spf}{$r}{$s}{$v}',"; + } + print OUT " },\n"; + } + print OUT "\t},\n"; + } + print OUT "},\n"; + print OUT "'dkim' => {\n"; + foreach my $r (keys %{$localspf_dkim{dkim}}) { + print OUT "\t'$r' => {\n"; + foreach my $s (keys %{$localspf_dkim{dkim}{$r}}) { + print OUT "\t\t'$s' => '$localspf_dkim{dkim}{$r}{$s}',\n"; + } + print OUT "\t},\n"; + } + print OUT "},\n"; + print OUT ");\n\n"; + %localspf_dkim = (); + print OUT "\%::virus = (\n"; print OUT "'inbound' => '$localvirus{inbound}',\n"; print OUT "'inbound_bytes' => '$localvirus{inbound_bytes}',\n"; @@ -4376,6 +4564,7 @@ sub do_week_cache my %localpostgrey = (); my %localtoppostgrey = (); my %localstarttls = (); + my %localspf_dkim = (); my $lbls = ''; my %authval = (); &clean_globals(); @@ -4448,6 +4637,20 @@ sub do_week_cache $localpostgrey{reason}{$k} += $postgrey{reason}{$k}; } %postgrey = (); + + foreach my $r (keys %{$spf_dkim{spf}}) { + foreach my $k (keys %{$spf_dkim{spf}{$r}{status}}) { + $localspf_dkim{spf}{$r}{status}{$k} += $spf_dkim{spf}{$r}{status}{$k}; + } + foreach my $k (keys %{$spf_dkim{spf}{$r}{domain}}) { + $localspf_dkim{spf}{$r}{domain}{$k} += $spf_dkim{spf}{$r}{domain}{$k}; + } + } + foreach my $r (keys %{$spf_dkim{dkim}{status}}) { + $localspf_dkim{dkim}{status}{$r} += $spf_dkim{dkim}{status}{$r}; + } + %spf_dkim = (); + foreach my $k (keys %virus) { if (!grep(/^$k$/, 'lbls','x_label','values')) { $localvirus{$k} += $virus{$k}; @@ -4673,6 +4876,32 @@ sub do_week_cache print OUT ");\n\n"; %localpostgrey = (); + print OUT "\%::spf_dkim = (\n"; + print OUT "'spf' => {\n"; + foreach my $r (keys %{$localspf_dkim{spf}}) { + print OUT "\t'$r' => {\n"; + foreach my $s (keys %{$localspf_dkim{spf}{$r}}) { + print OUT "\t\t'$s' => { "; + foreach my $v (keys %{$localspf_dkim{spf}{$r}{$s}}) { + print OUT "'$v' => '$localspf_dkim{spf}{$r}{$s}{$v}',"; + } + print OUT " },\n"; + } + print OUT "\t},\n"; + } + print OUT "},\n"; + print OUT "'dkim' => {\n"; + foreach my $r (keys %{$localspf_dkim{dkim}}) { + print OUT "\t'$r' => {\n"; + foreach my $s (keys %{$localspf_dkim{dkim}{$r}}) { + print OUT "\t\t'$s' => '$localspf_dkim{dkim}{$r}{$s}',\n"; + } + print OUT "\t},\n"; + } + print OUT "},\n"; + print OUT ");\n\n"; + %localspf_dkim = (); + print OUT "\%::virus = (\n"; print OUT "'inbound' => '$localvirus{inbound}',\n"; print OUT "'inbound_bytes' => '$localvirus{inbound_bytes}',\n"; @@ -4954,7 +5183,6 @@ sub do_week_cache print OUT ");\n\n"; %localtoppostgrey = (); - # Top virus statistics $top = 0; print OUT "\%::topvirus = (\n"; diff --git a/sendmailanalyzer b/sendmailanalyzer index ec208c3..f6d258c 100755 --- a/sendmailanalyzer +++ b/sendmailanalyzer @@ -91,6 +91,7 @@ my %AMAVIS_ID = (); my %STARTTLS = (); my %SPAMPD = (); my %KEEP_TEMPORARY = (); +my %SPF_DKIM = (); # Collect command line arguments GetOptions ( @@ -116,6 +117,7 @@ GetOptions ( 'z|zcat=s' => \$CONFIG{ZCAT_PROG}, 'y|year=s' => \$CONFIG{DEFAULT_YEAR}, 'spamd=s' => \$CONFIG{SPAMD_NAME}, + 'spf=s' => \$CONFIG{SPF_DKIM_NAME}, 'hostname=s' => \$HOSTNAME, ); @@ -250,6 +252,8 @@ sendmailanalyzer v$VERSION usage: Default /usr/bin/zcat. --spamd name : syslog Spamd program name. Default: spamd. --hostname name : set a hostname for exim logs. Default: unknown. + --spf name : syslog SPF and DKIM program name list. + Default: opendmarc|opendkim. }; exit 0; } @@ -350,7 +354,7 @@ sub start_loop my $tmp_last_parsed = $l; # Only catch relevant logs next if ($CONFIG{EXCLUDE_LINE} && $tmp_last_parsed =~ m#$CONFIG{EXCLUDE_LINE}#); - if ( ($tmp_last_parsed =~ /($CONFIG{MTA_NAME}|$CONFIG{MAILSCAN_NAME}|$CONFIG{AMAVIS_NAME}|$CONFIG{MD_NAME}|$CONFIG{CLAMD_NAME}|$CONFIG{POSTGREY_NAME}|$CONFIG{SPAMD_NAME}|$CONFIG{CLAMSMTPD_NAME})[\/\[:]/) || ($LAST_PARSED =~ $EXIM_REGEX) ) { + if ( ($tmp_last_parsed =~ /($CONFIG{MTA_NAME}|$CONFIG{MAILSCAN_NAME}|$CONFIG{AMAVIS_NAME}|$CONFIG{MD_NAME}|$CONFIG{CLAMD_NAME}|$CONFIG{POSTGREY_NAME}|$CONFIG{SPAMD_NAME}|$CONFIG{CLAMSMTPD_NAME}|$CONFIG{SPF_DKIM_NAME})[\/\[:]/) || ($LAST_PARSED =~ $EXIM_REGEX) ) { if ($tmp_last_parsed ne $OLD_LAST_PARSED) { &dprint("Size is identique but data are more recent than the one at old offset. Rereading from start of the log file.") if ($CONFIG{DEBUG} > 0); seek(SA_FILE, 0, 0); @@ -381,7 +385,7 @@ sub start_loop $l = ''; # Only catch relevant logs next if ($CONFIG{EXCLUDE_LINE} && $LAST_PARSED =~ m#$CONFIG{EXCLUDE_LINE}#); - if ( ($LAST_PARSED =~ /($CONFIG{MTA_NAME}|$CONFIG{MAILSCAN_NAME}|$CONFIG{AMAVIS_NAME}|$CONFIG{MD_NAME}|$CONFIG{CLAMD_NAME}|$CONFIG{POSTGREY_NAME}|$CONFIG{SPAMD_NAME}|$CONFIG{CLAMSMTPD_NAME})[\/\[:]/) || ($LAST_PARSED =~ $EXIM_REGEX) ) { + if ( ($LAST_PARSED =~ /($CONFIG{MTA_NAME}|$CONFIG{MAILSCAN_NAME}|$CONFIG{AMAVIS_NAME}|$CONFIG{MD_NAME}|$CONFIG{CLAMD_NAME}|$CONFIG{POSTGREY_NAME}|$CONFIG{SPAMD_NAME}|$CONFIG{CLAMSMTPD_NAME}|$CONFIG{SPF_DKIM_NAME})[\/\[:]/) || ($LAST_PARSED =~ $EXIM_REGEX) ) { my $tmpos = tell(SA_FILE); if ($OLD_LAST_PARSED) { # Store the last position in the log @@ -469,7 +473,7 @@ sub start_loop # Only catch relevant logs next if ($CONFIG{EXCLUDE_LINE} && $LAST_PARSED =~ m#$CONFIG{EXCLUDE_LINE}#); my $check_time = ''; - if ( ($LAST_PARSED =~ /($CONFIG{MTA_NAME}|$CONFIG{MAILSCAN_NAME}|$CONFIG{AMAVIS_NAME}|$CONFIG{MD_NAME}|$CONFIG{CLAMD_NAME}|$CONFIG{POSTGREY_NAME}|$CONFIG{SPAMD_NAME})[\/\[]/) || ($LAST_PARSED =~ $EXIM_REGEX) ) { + if ( ($LAST_PARSED =~ /($CONFIG{MTA_NAME}|$CONFIG{MAILSCAN_NAME}|$CONFIG{AMAVIS_NAME}|$CONFIG{MD_NAME}|$CONFIG{CLAMD_NAME}|$CONFIG{POSTGREY_NAME}|$CONFIG{SPAMD_NAME}|$CONFIG{CLAMSMTPD_NAME}|$CONFIG{SPF_DKIM_NAME})[\/\[]/) || ($LAST_PARSED =~ $EXIM_REGEX) ) { # Extract common fields and store data in memory $check_time = &store_data(&parse_common_fields(split(/\s+/, $LAST_PARSED))); } else { @@ -574,6 +578,8 @@ sub store_data &parse_postgrey("$date","$time",$host,$other); } elsif ($CONFIG{SPAMD_NAME} && ($type =~ /^$CONFIG{SPAMD_NAME}/i)) { &parse_spamd("$date","$time",$host,$other); + } elsif ($CONFIG{SPF_DKIM_NAME} && ($type =~ /^$CONFIG{SPF_DKIM_NAME}/i)) { + &parse_spf_dkim("$date","$time",$host,$other); } else { &dprint("Skipping unknown syslog report => $date $time $host [$type]: $other") if ($CONFIG{DEBUG} > 1); } @@ -2137,6 +2143,44 @@ sub parse_spamd } } +#### +# Parse DKIM/SPF syslog output +#### +sub parse_spf_dkim +{ + my ($date,$time,$host,$str) = @_; + + my $time_st = "$date$time"; + + #Jun 7 06:22:58 server opendmarc[980]: 2693E3640260: news.vendita--flash.com none + if ($str =~ /^([^:]+): SPF\(([^\)]+)\): ([^\s]+) ([^\s]+)$/) { + + my $id = $1; + $SPF_DKIM{$host}{spf}{$id}{rule} = $2; + $SPF_DKIM{$host}{spf}{$id}{status} = $4; + $SPF_DKIM{$host}{spf}{$id}{domain} = &clean_relay($3); + $SPF_DKIM{$host}{spf}{$id}{date} = $time_st; + + #Jun 7 06:22:58 server opendkim[953]: 2693E3640260: DKIM verification successful + } elsif ($str =~ /^([^:]+): DKIM (.*)$/) { + + my $id = $1; + $SPF_DKIM{$host}{dkim}{$id}{status} = $2; + $SPF_DKIM{$host}{dkim}{$id}{date} = $time_st; + + #Jun 7 08:43:51 server opendkim[953]: 07EE63640A96: DKIM-Signature field added (s=default, d=mydomain.com) + } elsif ($str =~ /^([^:]+): DKIM-([^\(]+) \(s=([^,]+), d=([^\)]+)\)$/) { + + my $id = $1; + $SPF_DKIM{$host}{dkim}{$id}{status} = $2; + $SPF_DKIM{$host}{dkim}{$id}{rule} = $3; + $SPF_DKIM{$host}{dkim}{$id}{domain} = $4; + $SPF_DKIM{$host}{dkim}{$id}{date} = $time_st; + } + +} + + #### # Decode email address and keep only email part #### @@ -2568,6 +2612,42 @@ sub flush_data # clear all postgrey memory storage %POSTGREY = (); + # SaveSPF/DKIM objects + &dprint("Writing SPF/DKIM detail data to disk..."); + $nobj = 0; + foreach my $host (keys %SPF_DKIM) { + my %spf_dkims = (); + foreach my $kind (keys %{$SPF_DKIM{$host}}) { + foreach my $id (keys %{$SPF_DKIM{$host}{$kind}}) { + if (exists $EXCLUDED{$id}) { + delete $SPF_DKIM{$host}{$kind}{$id}; + next; + } + next if ($SPF_DKIM{$host}{$kind}{$id}{date} !~ /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/); + # Key: year/month/day, Format: Hour:Id:type:rule:domain:status + $spf_dkims{"$1/$2/$3"} .= "$4$5$6" . ':' . $id . ':' . $kind . ':' . $SPF_DKIM{$host}{$kind}{$id}{rule} . ':' . $SPF_DKIM{$host}{$kind}{$id}{domain} . ':' . $SPF_DKIM{$host}{$kind}{$id}{status} . "\n"; + $nobj++; + } + } + foreach my $dir (keys %spf_dkims) { + if (!-d "$CONFIG{OUT_DIR}/$host/$dir") { + &create_directory("$host/$dir"); + } + if (not open(OUT, ">>$CONFIG{OUT_DIR}/$host/$dir/spf_dkim.dat") ) { + &logerror("Can't write to file $CONFIG{OUT_DIR}/$host/$dir/spf_dkim.dat: $!"); + &logerror("Data will be lost."); + next; + } else { + print OUT $spf_dkims{$dir}; + close(OUT); + } + } + } + &dprint("\tWrote $nobj spf/dkim object."); + # clear all spf/dkim memory storage + %SPF_DKIM = (); + + # Save Virus objects &dprint("Writing Virus data to disk..."); $nobj = 0; @@ -2840,6 +2920,7 @@ sub read_config $CONFIG{PID_DIR} ||= '/var/run'; $CONFIG{POSTGREY_NAME} ||= 'postgrey|sqlgrey'; $CONFIG{SPAMD_NAME} ||= 'spamd'; + $CONFIG{SPF_DKIM_NAME} ||= 'opendmarc|opendkim'; } #### @@ -3105,6 +3186,7 @@ sub clean_globals %STARTTLS = (); %SPAMPD = (); %KEEP_TEMPORARY = (); + %SPF_DKIM = (); }