[Mimedefang] DNS Lookups in MD - Was RBL and DNS lookups
Kevin A. McGrail
kmcgrail at pccc.com
Fri May 11 11:46:04 EDT 2007
> You really shouldn't have to do any DNS lookups until you run SA.
> Sendmail will have already done a reverse lookup on the connecting IP
> address (available in $RelayHostname), so there's nothing much else to
> look up.
I disagree. I run at least to routines in my filter which use DNS which I
find very effective.
The first is to check for valid MX records on the sender. I use this to
reject email and it works VERY well.
Get the perl module from CPAN. Net::validMX.
In filter_initialize, add
#for Check Valid MX
use Net::validMX qw(check_valid_mx);
in filter_sender add something like this:
if ($sender ne '<>') {
($rv, $reason) = &check_valid_mx($sender);
if ($rv) {
if ($reason =~ /Resolution Problem/i) {
md_syslog('warning', "Net::validMX Resolution Problem: $sender -
$reason.");
}
} else {
md_syslog('warning', "Rejecting $sender - Invalid MX: $reason.");
return ('REJECT', "Sorry; $sender has an invalid MX record:
$reason.");
}
}
The second is a reverse DNS check that I then use to score with in
SpamAssassin. It has some pros and cons. The pro is that it works very
well. Then con is that I score a negative on having a reverse ptr which
some spammers use. However, I use this to then submit the more
"business-like" spammers to URIBLs which I then exclude from the rule.
This is just a snippet more so than a drop and run. You'll need some
variables defined if in strict and you'll need use Net::DNS; in your
filter_initialize. And you'll probably want an authorized sender routine
that sets the authorized_sender variable. I also have a VERY inefficient
routine which David Skoll I believe cursed to the heavens but hasn't come up
with something better.
BTW, several years ago, someone helping me with RelayRegistry showed me how
to start making MD "mini" modules. I didn't understand what they meant then
but now I see the error of my ways. However, despite my searching, I can't
find the info. I'm looking to do something like put all the reverse DNS
info in a separate file and use a C-esque #INCLUDE.
#REVERSE DNS CHECK
if ($authorized_sender < 1) {
$res = Net::DNS::Resolver->new;
$suspect_spammy_country_tlds = 1;
if (defined ($res)) {
$res->tcp_timeout(30); #Number of Seconds before query
will fail
$res->udp_timeout(30); #Number of Seconds before query
will fail
#Perform a reverse DNS lookup and set headers for SpamAssassin
Scoring based on AOL's reverse DNS policy as of Sept/22/2006
#See http://postmaster.aol.com/info/rdns.html
$packet = $res->send($RelayAddr);
if (defined ($packet)) {
#print "No Error - May or may not have resolved. Check
ancount.\n";
if (defined ($packet->answer) && $packet->header->ancount) {
#HAS A REVERSE ENTRY
@answer = $packet->answer;
if ($answer[0]->type eq "PTR") {
$reverse = $answer[0]->{'ptrdname'};
#TO AVOID FAILING DYNDNS.ORG, ETC. WE ARE ONLY TESTING THE
SUBDOMAIN(s) (i.e. the part to the left of the domain)
$has_subdomain = ($reverse =~ s/\././g > 1);
if ($has_subdomain) {
$reverse_subdomain = $reverse;
$reverse_subdomain =~ s/[^\.]*\.[^\.]*$//;
}
if ($reverse =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/ or
$reverse !~ /\./ or $reverse =~ /in-addr.arpa/i) {
#FAILED REQUIREMENT HAD AN INVALID IP QUAD, CONTAINED
IN-ADDR.ARPA OR FAILED TO USE A FQDN
action_change_header("X-KAM-Reverse", "Failed - $reverse -
Reverse PTR was invalid ip quad, contained in-addr.arpa or failed to use a
FQDN");
&append_header_immediately(header=>"X-KAM-Reverse: Failed -
$reverse - Reverse PTR was invalid ip quad, contained in-addr.arpa or failed
to use a FQDN");
} elsif ($has_subdomain && $reverse_subdomain =~
/pool|dhcp|dip|dyn|dial|home|cable|dsl|\d{1,3}[-\.]\d{1,3}[-\.]\d{1,3}|\d{9,12}/i
&& $reverse_subdomain !~ /static/i) {
#REVERSE DNS SUBDOMAIN ENTRY IS SUSPECT
action_change_header("X-KAM-Reverse", "Suspect - $reverse -
Reverse PTR contains data that indicates it is a dynamic IP");
&append_header_immediately(header=>"X-KAM-Reverse: Suspect -
$reverse - Reverse PTR contains data that indicates it is a dynamic IP");
} elsif ($suspect_spammy_country_tlds > 0 && $reverse =~
/\.(nl|ru|br|ae|cz|cy|pl|il|hu|at|it|jp|de|ua|tw|fr|do|ch|sk|nu|lt|ro|gr|uy|tr|my|fi)$/)
{
#SPAMMY COUNTRIES
action_change_header("X-KAM-Reverse", "Suspect - $reverse -
Reverse PTR contains data that indicates it is outside the US");
&append_header_immediately(header=>"X-KAM-Reverse: Suspect -
$reverse - Reverse PTR contains data that indicates it is outside the US");
} else {
#VALID REVERSE DNS. SCORE AS HAM
action_change_header("X-KAM-Reverse", "Passed - Reverse DNS
of $reverse");
&append_header_immediately(header=>"X-KAM-Reverse: Passed -
Reverse DNS of $reverse");
}
}
} else {
#FAILED REQUIREMENT DID NOT HAVE A REVERSE ENTRY
action_change_header("X-KAM-Reverse", "Missing - Reverse PTR for
$RelayAddr was missing!");
&append_header_immediately(header=>"X-KAM-Reverse: Missing -
Reverse PTR for $RelayAddr was missing!");
}
} else {
#Undef = Error. DO NOT BASE ANY CODE ON THIS RETURN
}
}
}
#END REVERSE DNS CHECK
sub change_header_immediately {
my (%params) = @_;
&append_header_immediately(%params, change=>1);
#md_syslog('warning', "Changing $params{'header'} immediately");
}
sub append_header_immediately {
#IN ORDER TO HAVE A HEADER TEST IN SPAMASSASSIN REACT TO DATA THAT MD
CREATES, WE HAVE TO EDIT THE INPUTMSG FILE PRIOR TO
#CALLING SA. USING action_change_header, ETC WILL NOT WORK. NO CHANGES
TO THIS FILE ARE SAVED SO IF YOU WANT HEADERS
#ADDED OR APPENDED, USE STANDARD MD CALLS ALSO!!!:
#
# action_change_header('Date', $date);
# &change_header_immediately(header=>"Date: $date");
my (%params) = @_;
my ($filehandle, $output, $firstlineonly, $header);
$filehandle = new IO::File('+< ./INPUTMSG');
$firstlineonly = 1;
$params{'change'} ||= 0;
$params{'kilobyte_limit'} ||= 256;
$header = $params{'header'};
$header =~ s/^([^:.]*): .*$/$1/;
if (-s "./INPUTMSG" == 0) {
md_syslog('warning', "INPUTMSG is Size 0");
}
if (-s "./INPUTMSG" < $params{'kilobyte_limit'}*1024 && $filehandle) {
while (<$filehandle>) {
if ($params{'change'} > 0) {
if ($_ =~ /^$header:/) {
$output .= "$params{'header'}\n";
} else {
$output .= $_;
}
} elsif ($_ =~ /^$/ && $firstlineonly) {
$output .= "$params{'header'}\n$_";
$firstlineonly = 0;
} else {
$output .= $_;
}
}
if ($output eq '') {
md_syslog('warning', '$output is blank');
}
seek $filehandle, 0, 0;
print $filehandle $output;
close ($filehandle);
}
#md_syslog('warning', "Adding $params{'header'} immediately");
}
BTW, I'm not sure I completely understand why &'s are bad for function
calls. I don't use them because that is how Perl4 worked. I use them
because it makes the highlighting in VIM-Enhanced work better ;-)
Regards,
KAM
More information about the MIMEDefang
mailing list