274 lines
7.6 KiB
Perl
274 lines
7.6 KiB
Perl
# Adds DNSWL.org to recipients of spamassassin --report.
|
|
#
|
|
# In a SpamAssassin config file, add the lines:
|
|
#
|
|
# loadplugin Mail::SpamAssassin::Plugin::DNSWLh
|
|
# dnswl_address user@example.com
|
|
# dnswl_password yourpassword
|
|
#
|
|
# The last two must be from an account created via
|
|
# http://www.dnswl.org/registerreporter.pl
|
|
#
|
|
#
|
|
# 2010-02-26-23 Initial release.
|
|
# 2010-02-27-11 Also call report successful on unlisted IPs.
|
|
# 2010-02-28-20 State when reported email has trust level "Unlisted".
|
|
# 2010-03-02-10 Report the IP DNSWL thought was interesting.
|
|
|
|
# <@LICENSE>
|
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
|
# contributor license agreements. See the NOTICE file distributed with
|
|
# this work for additional information regarding copyright ownership.
|
|
# The ASF licenses this file to you under the Apache License, Version 2.0
|
|
# (the "License"); you may not use this file except in compliance with
|
|
# the License. You may obtain a copy of the License at:
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
# </@LICENSE>
|
|
|
|
=head1 NAME
|
|
|
|
Mail::SpamAssassin::Plugin::DNSWL - perform DNSWL reporting of messages
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
loadplugin Mail::SpamAssassin::Plugin::DNSWL
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
DNSWL is a service which lists known legitimate mail servers.
|
|
This module enables automatic reporting of spam to DNSWL, to improve
|
|
the accuracy of their database.
|
|
|
|
Note that spam reports sent by this plugin to DNSWL each include the
|
|
entire spam message.
|
|
|
|
See http://www.dnswl.org/ for more information about DNSWL.
|
|
|
|
=cut
|
|
|
|
package Mail::SpamAssassin::Plugin::DNSWLh;
|
|
|
|
use Mail::SpamAssassin::Plugin;
|
|
use Mail::SpamAssassin::Logger;
|
|
use IO::Socket;
|
|
use strict;
|
|
use warnings;
|
|
use bytes;
|
|
use re 'taint';
|
|
|
|
use constant HAS_LWP_USERAGENT => eval { require LWP::UserAgent; };
|
|
|
|
use vars qw(@ISA);
|
|
@ISA = qw(Mail::SpamAssassin::Plugin);
|
|
|
|
sub new {
|
|
my $class = shift;
|
|
my $mailsaobject = shift;
|
|
|
|
$class = ref($class) || $class;
|
|
my $self = $class->SUPER::new($mailsaobject);
|
|
bless ($self, $class);
|
|
|
|
# are network tests enabled?
|
|
if (!$mailsaobject->{local_tests_only} && HAS_LWP_USERAGENT) {
|
|
$self->{dnswl_available} = 1;
|
|
dbg("DNSWL: network tests on, attempting DNSWL");
|
|
}
|
|
else {
|
|
$self->{dnswl_available} = 0;
|
|
dbg("DNSWL: local tests only, disabling DNSWL");
|
|
}
|
|
|
|
$self->set_config($mailsaobject->{conf});
|
|
|
|
return $self;
|
|
}
|
|
|
|
sub set_config {
|
|
my($self, $conf) = @_;
|
|
my @cmds;
|
|
|
|
=head1 USER OPTIONS
|
|
|
|
=over 4
|
|
|
|
=cut
|
|
|
|
push (@cmds, {
|
|
setting => 'dnswl_address',
|
|
default => 'spamassassin-submit@spam.dnswl.chaosreigns.com',
|
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
|
|
code => sub {
|
|
my ($self, $key, $value, $line) = @_;
|
|
if ($value =~ /^([^<\s]+\@[^>\s]+)$/) {
|
|
$self->{dnswl_address} = $1;
|
|
}
|
|
elsif ($value =~ /^$/) {
|
|
return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
|
|
}
|
|
else {
|
|
return $Mail::SpamAssassin::Conf::INVALID_VALUE;
|
|
}
|
|
},
|
|
});
|
|
push (@cmds, {
|
|
setting => 'dnswl_password',
|
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
|
|
code => sub {
|
|
my ($self, $key, $value, $line) = @_;
|
|
if ($value =~ /^(\S+)$/) {
|
|
$self->{dnswl_password} = $1;
|
|
}
|
|
elsif ($value =~ /^$/) {
|
|
return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
|
|
}
|
|
else {
|
|
return $Mail::SpamAssassin::Conf::INVALID_VALUE;
|
|
}
|
|
},
|
|
});
|
|
|
|
=item dnswl_max_report_size (default: 50)
|
|
|
|
Messages larger than this size (in kilobytes) will be truncated in
|
|
report messages sent to DNSWL. The default setting is the maximum
|
|
size that DNSWL will accept at the time of release.
|
|
|
|
=cut
|
|
|
|
push (@cmds, {
|
|
setting => 'dnswl_max_report_size',
|
|
default => 50,
|
|
type => $Mail::SpamAssassin::Conf::CONF_TYPE_NUMERIC
|
|
});
|
|
|
|
$conf->{parser}->register_commands(\@cmds);
|
|
}
|
|
|
|
sub plugin_report {
|
|
my ($self, $options) = @_;
|
|
|
|
return unless $self->{dnswl_available};
|
|
|
|
#dbg("DNSWL: address/pass: " . $options->{report}->{conf}->{dnswl_address}
|
|
# .' '. $options->{report}->{conf}->{dnswl_password} );
|
|
|
|
if (!$options->{report}->{options}->{dont_report_to_dnswl}) {
|
|
if ($options->{report}->{conf}->{dnswl_address} and
|
|
$options->{report}->{conf}->{dnswl_password}) {
|
|
if ($self->dnswl_report($options)) {
|
|
$options->{report}->{report_available} = 1;
|
|
info("DNSWL: spam reported to DNSWL");
|
|
$options->{report}->{report_return} = 1;
|
|
} else {
|
|
info("DNSWL: could not report spam to DNSWL");
|
|
}
|
|
} else {
|
|
dbg("DNSWL: dnswl_address and/or dnswl_password not defined.");
|
|
}
|
|
}
|
|
}
|
|
|
|
sub dnswl_report {
|
|
my ($self, $options) = @_;
|
|
|
|
# original text
|
|
my $original = ${$options->{text}};
|
|
|
|
# check date
|
|
my $header = $original;
|
|
$header =~ s/\r?\n\r?\n.*//s;
|
|
my $date = Mail::SpamAssassin::Util::receive_date($header);
|
|
if ($date && $date < time - 2*86400) {
|
|
warn("DNSWL: Message older than 2 days, not reporting\n");
|
|
return 0;
|
|
}
|
|
|
|
# message variables
|
|
my $description = "spam report via " . Mail::SpamAssassin::Version();
|
|
my $trusted = $options->{msg}->{metadata}->{relays_trusted_str};
|
|
my $untrusted = $options->{msg}->{metadata}->{relays_untrusted_str};
|
|
|
|
# message data
|
|
|
|
# truncate message
|
|
if (length($original) > $self->{main}->{conf}->{dnswl_max_report_size} * 1024) {
|
|
substr($original, ($self->{main}->{conf}->{dnswl_max_report_size} * 1024)) =
|
|
"\n[truncated by SpamAssassin]\n";
|
|
}
|
|
|
|
my $body = <<"EOM";
|
|
Content-Description: $description
|
|
X-Spam-Relays-Trusted: $trusted
|
|
X-Spam-Relays-Untrusted: $untrusted
|
|
$original
|
|
EOM
|
|
|
|
# compose message
|
|
my $message;
|
|
$message = $body;
|
|
|
|
# send message
|
|
|
|
my %form = (
|
|
'action', 'save',
|
|
'abuseReport',$message,
|
|
);
|
|
|
|
my $ua = LWP::UserAgent->new;
|
|
|
|
my $netloc = 'www.dnswl.org:80';
|
|
my $realm = 'dnswl.org Abuse Reporting';
|
|
$ua->credentials( $netloc, $realm, $options->{report}->{conf}->{dnswl_address}, $options->{report}->{conf}->{dnswl_password} );
|
|
|
|
my $response = $ua->post('http://www.dnswl.org/abuse/report.pl', \%form);
|
|
# my $response = $ua->post('http://www.dnswl.org/abuse/report.test.pl', \%form);
|
|
# open OUT, ">/tmp/dnswlbody.".time.".txt";
|
|
# print OUT $form{'abuseReport'};
|
|
# close OUT;
|
|
|
|
if ($response->is_success) {
|
|
#if ( $response->content =~ m#Thank you for your report# ) {
|
|
if ( $response->content =~ m#IP ([\d\.]+) matches with DNSWL# ) {
|
|
my $reportedip = $1;
|
|
dbg("DNSWL: Successfully reported $reportedip.");
|
|
print "Successfully reported to DNSWL $reportedip.\n";
|
|
return 1;
|
|
#} elsif ( $response->content =~ m#No matching entry found for#) {
|
|
} elsif ( $response->content =~ m#No matching entry found for IP ([\d\.]+)#) {
|
|
my $reportedip = $1;
|
|
dbg("DNSWL: Successfully reported $reportedip. Current trust level is: Unlisted.");
|
|
print "Successfully reported to DNSWL $reportedip. Current trust level is: Unlisted.\n";
|
|
return 1;
|
|
} else {
|
|
dbg("DNSWL: Failed to report, acknowledgement not received.");
|
|
print "Failed to report to DNSWL, acknowledgement not received.\n";
|
|
# open OUT, ">/tmp/dnswlerr.".time.".txt";
|
|
# print OUT $response->content;
|
|
# close OUT;
|
|
return 0;
|
|
}
|
|
} else {
|
|
dbg("DNSWL: Failed to report: ". $response->status_line);
|
|
print "Failed to report to DNSWL, HTTP error: ". $response->status_line ."\n";
|
|
return 0;
|
|
}
|
|
|
|
dbg("DNSWL: Error: This isn't possible.");
|
|
return 0;
|
|
|
|
}
|
|
|
|
1;
|
|
|
|
=back
|
|
|
|
=cut
|