Folllow-up Test Code - Re: [Mimedefang] Potential for Business mail servers to nothavereverse DNS

John Rudd john at rudd.cc
Fri Sep 22 21:44:04 EDT 2006


Kevin A. McGrail wrote:
> use strict;
> use Net::DNS;

Looks decent.  I didn't use Net::DNS though (which basically just means 
I don't get to specify my own timeouts ... I should probably look into 
that). 

The other things I do differently:

a) it's a good idea that you only search the sub-domain for dynamic 
fingerprints... I hadn't thought about that; I might adapt to that. (so 
far, any shawcable emails I've bounced would have bounced for other 
reasons, but it has been my one worry about false-positives on the 
dynamic hostname front)

b) I look for elements of the IP address in the domain (or, in the 
sub-domain in your case).

Here's my filter_sender:

sub filter_sender {
   my ($sender, $ip, $hostname, $helo) = @_;
   my ($iaddr, $name, $aliases, $addrtype, $length, @addrs, $addr, $tmp);
   my $found = 0;

   # need this for checking for the SMTP-AUTH transaction
   read_commands_file();

   if ($sender =~ /\.local$/) {
      # no .local senders, even from my own IP addrs or authenticated 
senders

      md_syslog('warning', "Rejecting .local sender address $sender");
      return ('REJECT', "We do not accept .local sender addresses");
      }

   elsif ( (defined($SendmailMacros{'auth_authen'}))
        || ($ip =~ /^127\.0\.0\.1$/)
        || ($ip =~ /^128\.114\.125\./)
        || ($ip =~ /^128\.114\.2\.223/)
        || ($ip =~ /^10\.0\.1\./)
        || ($ip =~ /^69\.105\.229\.53$/)
        || ($ip =~ /^69\.12\.154\.165$/) ) {
      # exempt my home/work IP addrs or authenticated senders from 
further checks

      return ('CONTINUE', "ok");
      }


   elsif ($hostname ne "[$ip]") {
      # assume that $hostname was created from a rDNS check of $ip
      # so, we don't need to do that again with the gethostbyaddr
      # what we're doing is checking that $hostname isn't forged
      # by being sure that the @addrs it returns has $ip in it
      # (and, again, my own blocks or authentic senders are exempted above)

      ($name, $aliases, $addrtype, $length, @addrs) = 
gethostbyname($hostname);

      unless (defined ($name)) {
         md_syslog('warning',
                   "Tempfail can't verify reverse DNS for $hostname $ip");
         return ('TEMPFAIL',
                 "Can't verify that your reverse DNS matches your 
forward DNS" .
                    " - either your PTR record returns something that 
doesn't" .
                    " exist, or DNS for it is timing out - try again 
later");
         }
  
      foreach $addr (@addrs) {
         $tmp = join (".",  unpack('C4', $addr));
         if ($tmp eq $ip) {
            $found = 1;
            }
         }
  
      if (! ($found) ) { # we got an answer, it doesn't include $ip
         # ! $found should mean that the rDNS is forged to some other 
hostname
         md_syslog('warning', "Forged rDNS - $hostname doesn't return $ip");
         md_syslog('warning', "rDNS mismatch for $ip $name $hostname");
         return ('REJECT',
                 "Forged DNS: reverse DNS doesn't match forward DNS " .
                     "- fix your PTR and A records.");
         }
      else {
         # verified their reverse DNS matches, now lets do some checking
         # IP address components that might be in the hostname:
         my ($a, $b, $c, $d) = split(/\./, $ip);
         # decimal encoded IP address that might be in the hostname:
         my $e = ($a * 256 * 256 * 256) + ($b * 256 * 256) + ($c * 256) 
+ $d;
         # hexadecimal encoded IP address that might be in the hostname:
         my $f = lc(sprintf("%x", $a));
         my $g = lc(sprintf("%x", $b));
         my $h = lc(sprintf("%x", $c));
         my $i = lc(sprintf("%x", $d));
         my $j = lc(sprintf("%x", $e));

         #md_syslog('warning', "rDNS match for $ip $name $hostname");

         $hostname = lc($hostname);

         if ($hostname =~ /\.kr$/) {
            # korean hostnames only seem to send me spam
            md_syslog('warning', "Blocking .kr relay $hostname");
            return ('REJECT', "We don't accept email from .kr hostnames");
            }
         elsif ($hostname =~ /\.mx$/) {
            # mexican hostnames only seem to send me spam
            md_syslog('warning', "Blocking .mx relay $hostname");
            return ('REJECT', "We don't accept email from .mx hostnames");
            }
         elsif ( ($hostname =~ /(catv|cable|dsl|dhcp|ddns)/   ) ||
                 ($hostname =~ /(dial-?up|dynamic|ppp|$e|$j)/ ) ||
                 ($hostname =~ /($a.?0*$b|$b.?0*$c|$c.?0*$d)/ ) ||
                 ($hostname =~ /($d.?0*$c|$c.?0*$b|$b.?0*$a)/ ) ||
                 ($hostname =~ /($f.?0*$g|$g.?0*$h|$h.?0*$i)/ ) ||
                 ($hostname =~ /($i.?0*$h|$h.?0*$g|$g.?0*$f)/ ) ) {
            # The IP has a few key words in its hostname, or it has 
components
            # of its IP address in its hostname, then its probably an ISP
            # assigned dynamic or dial-up host, and should either get 
specific
            # DNS to avoid looking like a client, or use its ISP's mail 
server
            md_syslog('warning', "Blocking end-client host $hostname $ip");
            return ('REJECT',
                    "ISP clients IPs should use their ISPs mail server");
            }
         else { # otherwise, accept it
            return ('CONTINUE', "ok");
            }
         }
      }

   elsif ($hostname eq "[$ip]") {
      # Tempfail in case it was a transient DNS error.
      # (and, again, my own blocks or authentic senders are exempted above)

      md_syslog('warning', "Tempfail for no reverse DNS $ip");
      return ('TEMPFAIL',
              "You don't have valid reverse DNS - get a PTR record");
      }

   # we shouldn't get here ... the last two if conditions should cover
   # _every_ message that wasn't caught by the first 3, so, tempfail
   # if we get here
   return ('TEMPFAIL', "This shouldn't happen.");
   }





More information about the MIMEDefang mailing list