ระยะหลังๆ มานี่จะมี Spam แบบแปลกๆ เข้ามีโดยจะใช้ชื่อโดเมน .us, .me เป็นส่วนใหญ่ ตามตัวอย่าง
Received: from pure-garcinia-work.me (1974.clients.serverdeals.com [198.50.26.2])
by x (mailgw2) with ESMTP id SVE9H429848
for <x>; Sun, 01 Jun 2014 15:24:51 +0700
Date: Sun, 01 Jun 2014 01:23:47 -0700
Message-ID: <9017172.15147579@pure-garcinia-work.me>
From: MagicFatBurner <secretslim@pure-garcinia-work.me>
Content-Type: text/plain
To: <x>
Subject: 2014-Miracle, Dr.Oz-PerfectBody 100% Satisfaction Guaranteed.
Mime-Version: 1.0
It’s like turning your body into a calorie burning factory
without any of the extra work.
This One-Instant Trick Can Make The Difference
A More Fit You Is Possible if you go here now:
http://safe. pure-garcinia-work. me
และจะเป็นโดเมนที่เพิ่งจะจดมาได้ไม่นาน ซึ่งมันได้ฝ่าด่านเข้ามาใน Inbox จนได้เลยต้องหาวิธีสร้าง Rule ของ SpamAssassin ขึ้นมา แต่แล้วก็ไปเจอทางที่ง่ายกว่า คือ ไม่ว่าจะเปลี่ยนชื่อโดเมนเป็นอะไร หรือจดมาเมื่อไหร่ มันจะ Link ไปที่เว็บหลักของมันที่ IP เดิมๆ เพื่อ Redirect ต่อไปอีกที ก็เลยหาเขียน Plugin นี้ขึ้นมา เลยเอามาแบ่งกัน
สร้างไฟล์ URL2BadIP.pm
package Mail::SpamAssassin::Plugin::URLBadIP;
use Mail::SpamAssassin::Plugin;
use Mail::SpamAssassin::Constants qw(:ip);
#use Mail::SpamAssassin::Logger;
#use Data::Dumper;
use strict;
use warnings;
use bytes;
use re 'taint';
use vars qw(@ISA);
@ISA = qw(Mail::SpamAssassin::Plugin);
# constructor: register the eval rule
sub new {
my $class = shift;
my $mailsaobject = shift;
# some boilerplate...
$class = ref($class) || $class;
my $self = $class->SUPER::new($mailsaobject);
bless ($self, $class);
# the important bit!
$self->register_eval_rule("check_for_http_ip");
return $self;
}
###########################################################################
sub check_for_http_ip {
my ($self, $pms, $rule) = @_;
return 0;
}
sub check_post_dnsbl {
#my ($self,$pms, $rule) = @_;
my ($self, $opts) = @_;
my $pms = $opts->{permsgstatus};
my $msg = $opts->{msg};
my @a;
my $a;
my $b;
my $di;
my @urls;
my @ips;
my @hostip;
my @uniqueips;
my $IP_ADDRESS = IP_ADDRESS;
@urls = $pms->get_uri_list();
my @uniqueurls = uniq(@urls);
foreach my $uri (@uniqueurls) {
if ($uri =~ m{^(https?://)([^/]+?)((?::\d*)?\/.*)?$}i) {
my($proto, $host, $rest) = ($1,$2,$3);
dbg("debug:Found $host");
return 0 unless defined $host;
return 0 unless $pms->is_dns_available();
$self->load_resolver();
if ($host =~ /($IP_ADDRESS)/) {
push(@ips,$host);
} else {
my @hip = $self->lookup_a($host);
push(@ips, @hip);
}
}
}
@uniqueips = uniq(@ips);
foreach my $rule (keys(%{$pms->{conf}->{uri2badip}})) {
foreach my $ip (@uniqueips) {
@a = split /\./, $ip;
$di = getIp(@a);
if ($pms->{conf}->{uri2badip}->{$rule} =~ /\/\d/) { # search ip in CIDR range
($a,$b) = getNetwork($pms->{conf}->{uri2badip}->{$rule});
if (($di >= $a) && ($di <= $b)){
dbg ("hit rule: $rule");
$pms->got_hit($rule);
return 1;
}
} else {
if ($ip =~ /$pms->{conf}->{uri2badip}->{$rule}/) {
dbg("hit $rule");
$pms->got_hit($rule);
return 1;
}
}
}
}
return 0;
}
sub parse_config {
my ($self, $opts) = @_;
my $key = $opts->{key};
if ($key eq 'uri2badip') {
if ($opts->{value} =~ /^(\S+)\s+(\S+)\s*$/) {
my $rulename = $1;
my $ip = $2;
dbg("registering $rulename $ip");
$opts->{conf}->{uri2badip}->{$rulename} = $ip;
$self->inhibit_further_callbacks(); return 1;
}
}
return 0;
}
sub uniq {
my %seen;
grep !$seen{$_}++, @_;
}
sub getIp {
return ($_[0]*256*256*256) + ($_[1]*256*256) + ($_[2]*256) + $_[3];
}
sub getNetwork {
my @a = split(/[\/|\.]/, +shift);
return (getIp(@a[0 .. 3]), (getIp(@a[0 .. 3]) + (2 ** (32 - $a[4]))));
}
sub lookup_a {
my ($self, $name) = @_;
return undef unless $self->load_resolver();
if ($self->{main}->{local_tests_only}) {
dbg("dns: local tests only, not looking up A records");
return undef;
}
return if ($self->server_failed_to_respond_for_domain ($name));
dbg("dns: looking up A records for '$name'");
my @addrs;
if (exists $self->{dnscache}->{A}->{$name}) {
my $addrptr = $self->{dnscache}->{A}->{$name};
@addrs = @{$addrptr};
} else {
eval {
my $query = $self->{resolver}->send($name);
if ($query) {
foreach my $rr ($query->answer) {
if ($rr->type eq "A") {
push (@addrs, $rr->address);
}
}
}
$self->{dnscache}->{A}->{$name} = [ @addrs ];
1;
} or do {
my $eval_stat = $@ ne '' ? $@ : "errno=$!"; chomp $eval_stat;
dbg("dns: A lookup failed horribly, perhaps bad resolv.conf setting? (%s)", $eval_stat);
return undef;
};
}
dbg("dns: A records for '$name': " . join(' ',@addrs));
return @addrs;
}
sub load_resolver {
my ($self) = @_;
$self->{resolver} = $self->{main}->{resolver};
return $self->{resolver}->load_resolver();
}
sub server_failed_to_respond_for_domain {
my ($self, $dom) = @_;
if ($self->{dns_server_too_slow}->{$dom}) {
dbg("dns: server for '$dom' failed to reply previously, not asking again");
return 1;
}
return 0;
}
sub set_server_failed_to_respond_for_domain {
my ($self, $dom) = @_;
dbg("dns: server for '$dom' failed to reply, marking as bad");
$self->{dns_server_too_slow}->{$dom} = 1;
}
sub dbg { Mail::SpamAssassin::Plugin::dbg ("URLBadIP: @_"); }
1;
สร้างไฟล์ URL2BadIP.cf
ifplugin Mail::SpamAssassin::Plugin::URLBadIP
uri2badip uri2badip_192.31.186.3/32 192.31.186.3/32
header URI2BADIP1 eval:check_for_http_ip("192.31.186.3/32")
describe URI2BADIP1 URL pointed to spam ip
score URI2BADIP1 50.00
uri2badip uri2badip_66.96.243.38 66.96.243.38
header URI2BADIP2 eval:check_for_http_ip("66.96.243.38")
describe URI2BADIP2 URL pointed to spam ip
score URI2BADIP2 20.00
endif
เพิ่มบรรทัดในไฟล์ local.cf
loadplugin Mail::SpamAssassin::Plugin::URL2BadIP URL2BadIP.pm